diff --git a/src/node/SyncDish.mjs b/src/node/SyncDish.mjs
index a6d0a984..70f1e807 100644
--- a/src/node/SyncDish.mjs
+++ b/src/node/SyncDish.mjs
@@ -20,7 +20,7 @@ class SyncDish extends Dish {
/** */
constructor(inputOrDish=null, type=null) {
super(inputOrDish);
-
+
// Add operations to make it composable
for (const op in ops) {
this[op] = () => ops[op](this.value);
@@ -174,8 +174,4 @@ class SyncDish extends Dish {
}
}
-
-
-
-
export default SyncDish;
diff --git a/src/node/api.mjs b/src/node/api.mjs
index fd01dbc9..bedff90e 100644
--- a/src/node/api.mjs
+++ b/src/node/api.mjs
@@ -6,6 +6,9 @@
* @license Apache-2.0
*/
+/*eslint no-console: ["off"] */
+
+
import Dish from "../core/Dish";
import SyncDish from "./SyncDish";
import NodeRecipe from "./NodeRecipe";
@@ -199,29 +202,59 @@ export function wrap(OpClass) {
/**
* @namespace Api
- * @param {String} searchTerm - the name of the operation to get help for.
+ * help: Give information about operations matching the given search term,
+ * or inputted operation.
+ * @param {String || wrapped operation} input - the name of the operation to get help for.
* Case and whitespace are ignored in search.
- * @returns {Object} Describe function matching searchTerm.
+ * @returns {Object[]} Config of matching operations.
*/
-export function help(searchTerm) {
- let sanitised = false;
- if (typeof searchTerm === "string") {
- sanitised = searchTerm;
- } else if (typeof searchTerm === "function") {
- sanitised = searchTerm.opName;
+export function help(input) {
+ let searchTerm = false;
+ if (typeof input === "string") {
+ searchTerm = input;
+ } else if (typeof input === "function") {
+ searchTerm = input.opName;
}
- if (!sanitised) {
+ if (!searchTerm) {
return null;
}
- const key = Object.keys(OperationConfig)
- .find(o => sanitise(o) === sanitise(sanitised));
- if (key) {
- const result = OperationConfig[key];
- result.name = key;
- return result;
+ // Look for matches in operation name and description, listing name
+ // matches first.
+ const matches = Object.keys(OperationConfig)
+ // hydrate operation: swap op name for op config object (with name)
+ .map((m) => {
+ const hydrated = OperationConfig[m];
+ hydrated.name = m;
+
+ // Return hydrated along with what type of match it was
+ return {
+ hydrated,
+ nameMatch: sanitise(hydrated.name).includes(sanitise(searchTerm)),
+ descMatch: sanitise(hydrated.description).includes(sanitise(searchTerm))
+ };
+ })
+ // Filter out non-matches
+ .filter((result) => {
+ return result.nameMatch || result.descMatch;
+ })
+ // sort results with name match first
+ .sort((a, b) => {
+ const aInt = a.nameMatch ? 1 : 0;
+ const bInt = b.nameMatch ? 1 : 0;
+ return bInt - aInt;
+ })
+ // extract just the hydrated config
+ .map(result => result.hydrated);
+
+ // Concatenate matches but remove duplicates
+ if (matches && matches.length) {
+ console.log(`${matches.length} results found.`);
+ return matches;
}
+
+ console.log("No results found.");
return null;
}
diff --git a/src/node/index.mjs b/src/node/index.mjs
index 7a593826..7366d2fc 100644
--- a/src/node/index.mjs
+++ b/src/node/index.mjs
@@ -1045,7 +1045,7 @@ const operations = [
];
const prebaked = bake(operations);
-chef.bake = prebaked
+chef.bake = prebaked;
export default chef;
// Operations as top level exports.
diff --git a/test/tests/nodeApi/nodeApi.mjs b/test/tests/nodeApi/nodeApi.mjs
index 24145846..59747ea5 100644
--- a/test/tests/nodeApi/nodeApi.mjs
+++ b/test/tests/nodeApi/nodeApi.mjs
@@ -115,12 +115,12 @@ TestRegister.addApiTests([
it("chef.help: should describe a operation", () => {
const result = chef.help("tripleDESDecrypt");
- assert.strictEqual(result.name, "Triple DES Decrypt");
- assert.strictEqual(result.module, "Ciphers");
- assert.strictEqual(result.inputType, "string");
- assert.strictEqual(result.outputType, "string");
- assert.strictEqual(result.description, "Triple DES applies DES three times to each block to increase key size.
Key: Triple DES uses a key length of 24 bytes (192 bits).
DES uses a key length of 8 bytes (64 bits).
IV: The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.
Padding: In CBC and ECB mode, PKCS#7 padding will be used.");
- assert.strictEqual(result.args.length, 5);
+ assert.strictEqual(result[0].name, "Triple DES Decrypt");
+ assert.strictEqual(result[0].module, "Ciphers");
+ assert.strictEqual(result[0].inputType, "string");
+ assert.strictEqual(result[0].outputType, "string");
+ assert.strictEqual(result[0].description, "Triple DES applies DES three times to each block to increase key size.
Key: Triple DES uses a key length of 24 bytes (192 bits).
DES uses a key length of 8 bytes (64 bits).
IV: The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.
Padding: In CBC and ECB mode, PKCS#7 padding will be used.");
+ assert.strictEqual(result[0].args.length, 5);
}),
it("chef.help: null for invalid operation", () => {
@@ -130,8 +130,29 @@ TestRegister.addApiTests([
it("chef.help: takes a wrapped operation as input", () => {
const result = chef.help(chef.toBase32);
- assert.strictEqual(result.name, "To Base32");
- assert.strictEqual(result.module, "Default");
+ assert.strictEqual(result[0].name, "To Base32");
+ assert.strictEqual(result[0].module, "Default");
+ }),
+
+ it("chef.help: returns multiple results", () => {
+ const result = chef.help("base 64");
+ assert.strictEqual(result.length, 8);
+ }),
+
+ it("chef.help: looks in description for matches too", () => {
+ // string only in one operation's description.
+ const result = chef.help("Converts a unit of data to another format.");
+ assert.strictEqual(result.length, 1);
+ assert.strictEqual(result[0].name, "Convert data units");
+ }),
+
+ it("chef.help: lists name matches before desc matches", () => {
+ const result = chef.help("MD5");
+ assert.ok(result[0].name.includes("MD5"));
+ assert.strictEqual(result[1].name.includes("MD5"), false);
+ assert.strictEqual(result[2].name.includes("MD5"), false);
+ assert.ok(result[1].description.includes("MD5"));
+ assert.ok(result[2].description.includes("MD5"));
}),
it("chef.bake: should exist", () => {
diff --git a/test/tests/nodeApi/ops.mjs b/test/tests/nodeApi/ops.mjs
index e0793295..70a6ceac 100644
--- a/test/tests/nodeApi/ops.mjs
+++ b/test/tests/nodeApi/ops.mjs
@@ -512,7 +512,7 @@ Top Drawer`, {
const expected = "{\"dataLength\":43,\"percentages\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13.953488372093023,0,0,0,0,0,0,2.3255813953488373,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2.3255813953488373,4.651162790697675,2.3255813953488373,0,0,0,2.3255813953488373,0,0,0,0,0,0,0,0,0,0,0,2.3255813953488373,0,0,0,0,2.3255813953488373,0,0,0,0,0,0,0,2.3255813953488373,0,4.651162790697675,0,9.30232558139535,2.3255813953488373,0,6.976744186046512,2.3255813953488373,0,2.3255813953488373,0,0,6.976744186046512,9.30232558139535,0,0,4.651162790697675,2.3255813953488373,6.976744186046512,4.651162790697675,0,0,0,2.3255813953488373,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"distribution\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,2,0,4,1,0,3,1,0,1,0,0,3,4,0,0,2,1,3,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"bytesRepresented\":22}";
assert.strictEqual(result.toString(), expected);
}),
-
+
it("From base", () => {
assert.strictEqual(chef.fromBase("11", {radix: 13}).toString(), "14");
}),