---
title: "YAML validation: required keys and types"
author: "Package cre.dcf"
output:
  rmarkdown::html_vignette:
    toc: true
    number_sections: true
vignette: >
  %\VignetteIndexEntry{YAML validation: required keys and types}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---


```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)

library(cre.dcf)
library(yaml)

```

## Purpose

This vignette illustrates the internal validation mechanism used to ensure that configuration files loaded from YAML are both complete and type-consistent with the model’s expectations.
Such validation is essential for reproducibility: it guarantees that any downstream computation of discounted-cash-flows relies on a syntactically correct and semantically coherent configuration object.

From a methodological perspective, strict configuration validation contributes to:

Model reliability – early detection of malformed input values (e.g., string where numeric expected).

Scientific transparency – enforcement of explicit parameter names and units.

Reproducible computation – prevention of silent coercion errors that would bias model outputs.

The example below first validates a correct configuration, then deliberately introduces an error to verify that the control mechanism fails safely and predictably.

## 1. Load and validate a correct YAML configuration

```{r}
## 1. Load and validate a correct YAML configuration

# 1.1 Locate and parse a reference configuration file

path <- system.file("extdata", "preset_core.yml", package = "cre.dcf")
stopifnot(nzchar(path))

cfg <- yaml::read_yaml(path)
stopifnot(is.list(cfg), length(cfg) > 0)

# 1.2 Validate structure and types

# cfg_validate() throws an error if invalid; otherwise it returns (optionally invisibly)

# a configuration list that is deemed structurally consistent.

validated_cfg <- cre.dcf::cfg_validate(cfg)

cat("✓ Validation successful: configuration passed all structural and type checks.\n")

# 1.3 For illustration, display a compact excerpt of the validated configuration.

# Some implementations of cfg_validate() return cfg invisibly; others may return NULL.

# We therefore fall back to the original cfg if needed.

cfg_to_show <- if (is.list(validated_cfg) && length(validated_cfg) > 0L) {
validated_cfg
} else {
cfg
}

cat("\nExcerpt of (validated) configuration structure:\n")
utils::str(cfg_to_show[1:10], max.level = 1)

```

The call to cfg_validate() ensures, among other things, that:

purchase_year is an integer-like scalar,

numerical parameters such as index_rate, entry_yield, acq_cost_rate, or exit_yield_spread_bps lie in appropriate ranges,

mandatory blocks (discount-rate specification, debt structure, leases) are present and correctly typed.

Any violation triggers an error at this stage, before the configuration is used to construct cash-flow tables via run_case().

## Deliberate type violation and controlled failure

To test the robustness of the validation mechanism, we now introduce an explicit type error into a copy of the configuration.
The field purchase_year is expected to be an integer-like scalar; replacing it by a character string should therefore cause cfg_validate() to fail.

```{r}
## 2. Deliberate type violation and controlled failure

# 2.1 Clone configuration and introduce a deliberate type error:

# purchase_year must be integer-like; we turn it into a character string.

bad_cfg <- cfg
bad_cfg$purchase_year <- "2020"  # invalid type: character instead of integer/numeric

# 2.2 Attempt validation and capture the expected error

caught <- FALSE
err_msg <- NULL

tryCatch(
{
cre.dcf::cfg_validate(bad_cfg)
},
error = function(e) {
caught  <<- TRUE
err_msg <<- e$message
}
)

# 2.3 Assert that the failure mechanism was triggered

stopifnot(caught)

cat("\nExpected validation failure caught:\n")
cat(err_msg, "\n")

cat("\n✓ Error successfully detected: invalid type for 'purchase_year' was blocked at validation stage.\n")

```

## Interpretation

Configuration validation is more than a coding convenience. It helps keep runs comparable and makes input errors visible early.

In `cre.dcf`, this helps ensure:

- reproducibility: the same YAML input yields the same type of model object;

- comparability across simulations: output changes come from explicit parameter changes, not silent coercions;

- auditability: reviewers can check input validity without reading the engine internals.
