---
title: "Create Custom Assertions"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{create_custom_assertions}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

## Getting Started

Do you have a custom assertion that you want to use repeatedly in your code? Then create your own assertion functions with the `assert_create()` function!

Lets start by recreating the `assert_numeric()` assertion using `assert_create()`:

```{r, eval=FALSE}
# Load library
library(assertions)

# Create a function that asserts input is numeric
assert_numeric_2 <- assert_create(
  func = is.numeric, # Returns TRUE/FALSE for assertion PASS/FAIL
  default_error_msg = "'{arg_name}' must be of type numeric, not {class(arg_value)}"
  )

# Assertion passes if input is numeric
assert_numeric_2(123)

# But throws the expected error if input is not numeric
assert_numeric_2("abc")

```

To create a custom assertion function, you need to supply two arguments to assert_create():

`func`: a **function** that take an object to assert and returns TRUE or FALSE depending on whether the assertion should **pass** or **fail**.

`default_error_msg`: a **character string** providing an **error message** in case the assertion **fails**. This string can include special termss such as:

1.  **{arg_name}** to refer to the name of the variable being checked
2.  **{arg_value}** to refer to the value of the variable.
3.  **{code_to_evaluate}** to evaluate code within the error message. Customise '*code_to_evaluate*'. e.g `{class(arg_name)}`
4.  **{.strong bold_text}** to perform inline formatting. Customise '*bold_text*'

## Advanced Assertions

**Problem:**

Sometimes it is necessary to perform several assertions on a single object, and return error messages specific to the mode of failure. In these cases, it can be useful to chain together a series of different assertions on the object.

**Solution:**

The `assert_create_chain()` function allows you to combine multiple assertion functions created with `assert_create()` into a single assertion. Each of the wrapped assertions are evaluated in the order they are supplied, and if any of the assertions fail, the appropriate error message is returned.

**Example**

Here's an example of how to use `assert_create_chain()` to create an assertion function that asserts input is a string by individually asserting that

1.  Input is a 'character' type

2.  Input length is 1

```{r, eval=FALSE}
assert_string <- assert_create_chain(
  assert_create(is.character, '{arg_name} must be a character, not {class(arg_value)}'),
  assert_create(function(s){ length(s)==1 }, '{arg_name} must be length 1, not {arg_value}')
)

# Assert String
assert_string("String")

assert_string(3)
# Output: Error: '3' must be a character
```


## More Advanced Assertions

**Problem:**

We often need error messages to vary significantly based on the input. 

**Solution**

In such cases, it is more convenient to define the error messages in the function you pass to `func`. 
How does this work?

In our call to `assert_create`, instead of defining a `default_error_msg`, we can design `func` to return a string when the assertion should fail. This string will become the error message. By returning different strings upon different failure conditions, we can produce very diverse error messages very easily.

**Example**

Here's a recreation of the example above, using a `func` that supplies strings to indicate assertion failure

```{r, eval=FALSE}
# Define Function
is_a_string <- function(object){
 if(!is.character(object))
   return("{arg_name} must be a character, not {class(arg_value)}")
  
  if(length(object) != 1){
    return("{arg_name} must be length 1, not {length(object)}")
  }
  
  return(TRUE)
}

# Create Assertion
assert_is_string <- assert_create(
 is_a_string
)

# Test assertion works
assert_is_string("String")

assert_is_string(3)
# 3 must be a character, not numeric

assert_is_string(c("A", "B"))
```

**Additional Notes**

Note that in your error strings can use the special terms such as `{arg_name}`, but you will NOT have access to the first argument using its original name (e.g. `{object}`, in the example above). This is because assert_create changes this first arguments name.

Values of all other arguments can be referred to in string using `{name_of_nonfirst_argument}`