r-lib / rlang

Low-level API for programming with R
https://rlang.r-lib.org
Other
502 stars 139 forks source link

[FR] Allow showing expression instead of values in `arg_match()` error message #1686

Open mikmart opened 7 months ago

mikmart commented 7 months ago

Sometimes I need to match an argument against a long (100+) list of valid values. Currently the arg_match() error message will be overwhelmed by listing out all possible valid values. In such cases, it would be useful to have a way to include the expression from the formal argument in the message instead.

For example with:

foo <- function(dataset = ls("package:datasets")) {
  rlang::arg_match(dataset)
}

foo("Titanci")
#> Error in `foo()`:
#> ! `dataset` must be one of "ability.cov", "airmiles", "AirPassengers",
#>   "airquality", "anscombe", "attenu", "attitude", "austres", "beaver1",
#>   "beaver2", "BJsales", "BJsales.lead", "BOD", "cars", "ChickWeight",
#>   "chickwts", "co2", "CO2", "crimtab", "discoveries", "DNase", "esoph", "euro",
#>   "euro.cross", "eurodist", "EuStockMarkets", "faithful", "fdeaths",
#>   "Formaldehyde", "freeny", "freeny.x", "freeny.y", "HairEyeColor",
#>   "Harman23.cor", "Harman74.cor", "Indometh", "infert", "InsectSprays", "iris",
#>   "iris3", "islands", "JohnsonJohnson", "LakeHuron", "ldeaths", "lh",
#>   "LifeCycleSavings", "Loblolly", "longley", "lynx", "mdeaths", "morley",
#>   "mtcars", "nhtemp", "Nile", "nottem", "npk", "occupationalStatus", "Orange",
#>   "OrchardSprays", "PlantGrowth", "precip", "presidents", "pressure",
#>   "Puromycin", "quakes", "randu", "rivers", "rock", "Seatbelts", "sleep",
#>   "stack.loss", "stack.x", "stackloss", "state.abb", "state.area",
#>   "state.center", "state.division", "state.name", "state.region", "state.x77",
#>   "sunspot.month", "sunspot.year", "sunspots", "swiss", "Theoph", "Titanic",
#>   "ToothGrowth", "treering", "trees", "UCBAdmissions", "UKDriverDeaths",
#>   "UKgas", "USAccDeaths", "USArrests", "UScitiesD", "USJudgeRatings",
#>   "USPersonalExpenditure", "uspop", "VADeaths", "volcano", "warpbreaks",
#>   "women", "WorldPhones", or "WWWusage", not "Titanci".
#> ℹ Did you mean "Titanic"?

It would be nice to be able to instead get:

foo("Titanci")
#> Error in `foo()`:
#> ! `dataset` must be one of `ls("package:datasets")`, not "Titanci".
#> ℹ Did you mean "Titanic"?
olivroy commented 2 months ago

Alternatively,

Could arg_match() also trim down the number of choices if there are many, like cli does with its {.or }

arg_match0("28", as.character(1:27))
#> Error:
#> ! `"28"` must be one of "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
  "21", "22", "23", "24", "25", "26", or "27", not "28".
#> ℹ Did you mean "18"?
cli::cli_text("{.or {.str {as.character(1:27)}}}")
#> "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", …, "26", or "27"