Initial commit
commit
767d84908f
@ -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.
|
@ -0,0 +1,14 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const matchValue = require("./");
|
||||||
|
|
||||||
|
let flag = "a"; // Hardcoded for example
|
||||||
|
|
||||||
|
let mode = matchValue(flag, {
|
||||||
|
r: "readOnly",
|
||||||
|
rw: "readWrite",
|
||||||
|
a: "appendOnly",
|
||||||
|
_: () => `unknown:${someExpensiveCall(flag)}`
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(mode); // appendOnly
|
@ -0,0 +1,30 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
function getValue(value, functionsAreLiterals) {
|
||||||
|
if (typeof value === "function" && !functionsAreLiterals) {
|
||||||
|
return value();
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doMatchValue(value, arms, functionsAreLiterals) {
|
||||||
|
if (value == null) {
|
||||||
|
return value;
|
||||||
|
} else if (arms[value] !== undefined) {
|
||||||
|
// NOTE: We intentionally only check for `undefined` here (and below), since we want to allow the mapped-to value to be an explicit `null`.
|
||||||
|
return getValue(arms[value], functionsAreLiterals);
|
||||||
|
} else if (arms._ !== undefined) {
|
||||||
|
return getValue(arms._, functionsAreLiterals);
|
||||||
|
} else {
|
||||||
|
throw new Error(`No match arm found for value '${value}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function matchValue(value, arms) {
|
||||||
|
return doMatchValue(value, arms, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.literal = function matchValueLiteral(value, arms) {
|
||||||
|
return doMatchValue(value, arms, false);
|
||||||
|
};
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "match-value",
|
||||||
|
"description": "Utility for mapping values to other values or behaviour, a bit like a match statement",
|
||||||
|
"keywords": ["match", "switch", "case", "map", "mapping"],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"repository": "http://git.cryto.net/joepie91/match-value.git",
|
||||||
|
"author": "Sven Slootweg <admin@cryto.net>",
|
||||||
|
"license": "WTFPL OR CC0-1.0"
|
||||||
|
}
|
Loading…
Reference in New Issue