|
|
|
@ -0,0 +1,130 @@
|
|
|
|
|
# match-value
|
|
|
|
|
|
|
|
|
|
Since JS as a language does not (yet) have a match statement or expression, this package implements the same concept for the most common cases.
|
|
|
|
|
|
|
|
|
|
You specify a mapping of source values to result values, as an object, and `match-value` maps from one to the other, throwing an error if an invalid source value is specified, optionally having a catch-all for unknown values, and with support for lazily-produced result values.
|
|
|
|
|
|
|
|
|
|
In other words, it turns this:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
let mode;
|
|
|
|
|
|
|
|
|
|
if (flag === "r") {
|
|
|
|
|
mode = "readOnly";
|
|
|
|
|
} else if (flag === "rw") {
|
|
|
|
|
mode = "readWrite";
|
|
|
|
|
} else if (flag === "a") {
|
|
|
|
|
mode = "appendOnly";
|
|
|
|
|
} else {
|
|
|
|
|
mode = `unknown:${someExpensiveCall(flag)}`;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
... or this:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
let mode;
|
|
|
|
|
|
|
|
|
|
switch (flag) {
|
|
|
|
|
case "r":
|
|
|
|
|
mode = "readOnly";
|
|
|
|
|
break;
|
|
|
|
|
case "rw":
|
|
|
|
|
mode = "readWrite";
|
|
|
|
|
break;
|
|
|
|
|
case "a":
|
|
|
|
|
mode = "appendOnly";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
mode = `unknown:${someExpensiveCall(flag)}`;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
... into this:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const matchValue = require("match-value");
|
|
|
|
|
|
|
|
|
|
let mode = matchValue(flag, {
|
|
|
|
|
r: "readOnly",
|
|
|
|
|
rw: "readWrite",
|
|
|
|
|
a: "appendOnly",
|
|
|
|
|
_: () => `unknown:${someExpensiveCall(flag)}`
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Less repetition, and a much clearer intention.
|
|
|
|
|
|
|
|
|
|
## Limitations
|
|
|
|
|
|
|
|
|
|
- Source values may only be strings. Any input that isn't a string will always throw an error (or hit the catch-all, if you've specified one).
|
|
|
|
|
- This is unfortunately to do with a limitation of JS as a language, that makes it impossible to ergonomically specify mappings where the source value isn't a string key. If JS ever gets Map literals, this limitation can be fixed.
|
|
|
|
|
- Source values may only be literal values. There's no type/shape matching, like you might be used to if you've used a `match` construct in another language.
|
|
|
|
|
- Again, this is due to a language limitation.
|
|
|
|
|
- This library is completely unaware of the existence of Promises or asynchronous functions; all results are expected to be produced *synchronously*.
|
|
|
|
|
- Of course, it is completely valid to return a Promise as your result value, and structure your code like `Promise.resolve(matchValue(input, { ... })` to consistently get a Promise out of it.
|
|
|
|
|
|
|
|
|
|
## License, donations, and other boilerplate
|
|
|
|
|
|
|
|
|
|
Licensed under either the [WTFPL](http://www.wtfpl.net/txt/copying/) or [CC0](https://creativecommons.org/publicdomain/zero/1.0/), at your choice. In practice, that means it's more or less public domain, and you can do whatever you want with it. Giving credit is *not* required, but still very much appreciated! I'd love to [hear from you](mailto:admin@cryto.net) if this module was useful to you.
|
|
|
|
|
|
|
|
|
|
Creating and maintaining open-source modules is a lot of work. A donation is also not required, but much appreciated! You can donate [here](http://cryto.net/~joepie91/donate.html).
|
|
|
|
|
|
|
|
|
|
## Example
|
|
|
|
|
|
|
|
|
|
A full version of the example above:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
const matchValue = require("match-value");
|
|
|
|
|
|
|
|
|
|
let flag = "a"; // Hardcoded for example
|
|
|
|
|
|
|
|
|
|
let mode = matchValue(flag, {
|
|
|
|
|
r: "readOnly",
|
|
|
|
|
rw: "readWrite",
|
|
|
|
|
a: "appendOnly",
|
|
|
|
|
_: () => `unknown:${someExpensiveCall(flag)}`
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log(mode); // appendOnly
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## API
|
|
|
|
|
|
|
|
|
|
### matchValue(input, mapping)
|
|
|
|
|
|
|
|
|
|
Converts the `input` to a result based on the `mapping`.
|
|
|
|
|
|
|
|
|
|
- __input:__ The value to convert.
|
|
|
|
|
- __mapping:__ An object that describes the mapping in the format `{ possibleInput: result }`.
|
|
|
|
|
- A `possibleInput` of `_` defines a "catch-all" case; anything that doesn't match any of the other possible inputs, will trigger the catch-all case.
|
|
|
|
|
- The `result` may be either a literal value, or a function that *produces* that value. If it's a function, the function will *only* be called when there is a match, and the return value of that call will be returned from the `matchValue` call. If you want to *return* functions rather than call them, see the `.literal` method below.
|
|
|
|
|
|
|
|
|
|
Returns a result if a match was found, or the catch-all was hit. Throws an `Error` if no match was found *and* no catch-all was specified.
|
|
|
|
|
|
|
|
|
|
To throw a custom error type instead, define a catch-all function that throws that error, like so:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
matchValue(input, {
|
|
|
|
|
someKey: "someResult",
|
|
|
|
|
someOtherKey: "someOtherResult",
|
|
|
|
|
_: () => { throw new CustomError("Oh no!"); }
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Note that if you're using an arrow function here, it *must* have curly braces around the `throw`; since a `throw` in JS is a statement, not an expression, it may not appear in a brace-less arrow function (which only accepts expressions).
|
|
|
|
|
|
|
|
|
|
### matchValue.literal(input, mapping)
|
|
|
|
|
|
|
|
|
|
Exactly like `matchValue` above, but if the result value is a function, it will be *returned as-is*, rather than being called; that is, the function *is* the result.
|
|
|
|
|
|
|
|
|
|
Note that this also means that a custom error-throwing handler is not possible in this case.
|
|
|
|
|
|
|
|
|
|
## Changelog
|
|
|
|
|
|
|
|
|
|
### v1.0.0 (March 23, 2020)
|
|
|
|
|
|
|
|
|
|
Initial release.
|