Initial commit

master
Sven Slootweg 4 years ago
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…
Cancel
Save