---
title: "Condition handling"
author: "Colin Fay"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Condition handling}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

## warnings and messages

The `stop_if()`, `warn_if()` and `message_if()` are easy to use functions that send an error, a warning or a message if a condition is met. Each function has its counterpart with `_not` that returns a message if the condition is not met.

`stop_if_not()` is quite the same as `assert_that()` from the `{assertthat}` package, except that it can takes mappers. It is not the same as base `stopifnot()`, as it doesn't take a list of expression.

These functions are also flexible as you can pass base predicates (is.numeric, is.character...), a custom predicate built with mappers, or even your own predicate function.

You can either choose a custom message or just let the built-in messages be printed:

```{r}
x <- 12
# Stop if .x is numeric
stop_if(.x = x, 
        .p = is.numeric)

y <- "20"
# stop if .x is not numeric
stop_if_not(.x = y, 
            .p = is.numeric, 
            msg = "y should be numeric")
a  <- "this is not numeric"
# Warn if .x is charcter
warn_if(.x = a, 
        .p = is.character)

b  <- 20
# Warn if .x is not equal to 10
warn_if_not(.x = b, 
        .p = ~ .x == 10 , 
        msg = "b should be 10")

c <- "a"
# Message if c is a character
message_if(.x = c, 
           .p = is.character, 
           msg = "You entered a character element")

# Build more complex predicates
d <- 100
message_if(.x = d, 
           .p = ~ sqrt(.x) < 42, 
           msg = "The square root of your element must be more than 42")

# Or, if you're kind of old school, you can still pass classic functions

e <- 30
message_if(.x = e, 
           .p = function(vec){
             return(sqrt(vec) < 42)
           }, 
           msg = "The square root of your element must be more than 42")
```

If you need to call a function that takes no argument at `.p` (like `curl::has_internet()`), use this function as `.x`. 

```{r}
stop_if(.x = curl::has_internet(), msg = "You shouldn't have internet to do that")

warn_if(.x = curl::has_internet(), 
            msg = "You shouldn't have internet to do that")

message_if(.x = curl::has_internet(), 
            msg = "Huray, you have internet \\o/")
```

If you don't specify a `.p`, the default test is `isTRUE()`.

```{r}
a <- is.na(airquality$Ozone)
message_if_any(a, msg = "NA found")
```

### In function

That can come really handy inside a function: 

```{r}
my_fun <- function(x){
  stop_if_not(.x = curl::has_internet(), 
              msg = "You should have internet to do that")
  warn_if_not(x, 
          is.character, 
          msg =  "x is not a character vector. The output may not be what you're expecting.")
  paste(x, "is the value.")
}

my_fun(head(iris))
```

### none, all, any

`stop_if()`, `warn_if()` and `message_if()` all have complementary tests with `_all`, `_any` and `_none`, which combine the `if_*` and the `warn_*`, `stop_*` and `message_*` seen before. They take a list as first argument, and a predicate. They test if any, all or none of the elements validate the predicate. 

```{r}
stop_if_any(iris, is.factor, msg = "Factors here. This might be due to stringsAsFactors.")

warn_if_none(1:10, ~ .x < 0, msg = "You need to have at least one number under zero.")

message_if_all(1:100, is.numeric, msg = "That makes a lot of numbers.")
```

## `on_error()`

`on_error()` behaves as `on.exit()` except it happens only when there is an error in the function. 

``` r
y <- function(x){
  on_error(~ print("ouch"))
  log(x)
}
y(12)
[1] 2.484907
y("a")
Error in log(x) : non-numeric argument to mathematical function
[1] "ouch"
```
