const fs = require('fs');
const path = require('path');
-function getNextStep(content, pos, stop) {
- while (pos < content.length && content[pos] !== stop &&
- (content[pos] === ' ' || content[pos] === '\t' || content[pos] === '\n')) {
- pos += 1;
- }
- if (pos >= content.length) {
- return null;
- }
- if (content[pos] !== stop) {
- return pos * -1;
- }
- return pos;
-}
-
-// Stupid function extractor based on indent. Doesn't support block
-// comments. If someone puts a ' or an " in a block comment this
-// will blow up. Template strings are not tested and might also be
-// broken.
-function extractFunction(content, functionName) {
- var level = 0;
- var splitter = "function " + functionName + "(";
- var stop;
- var pos, start;
-
- while (true) {
- start = content.indexOf(splitter);
- if (start === -1) {
- break;
- }
- pos = start;
- while (pos < content.length && content[pos] !== ')') {
- pos += 1;
- }
- if (pos >= content.length) {
- break;
- }
- pos = getNextStep(content, pos + 1, '{');
- if (pos === null) {
- break;
- } else if (pos < 0) {
- content = content.slice(-pos);
- continue;
- }
- while (pos < content.length) {
- // Eat single-line comments
- if (content[pos] === '/' && pos > 0 && content[pos - 1] === '/') {
- do {
- pos += 1;
- } while (pos < content.length && content[pos] !== '\n');
-
- // Eat multiline comment.
- } else if (content[pos] === '*' && pos > 0 && content[pos - 1] === '/') {
- do {
- pos += 1;
- } while (pos < content.length && content[pos] !== '/' && content[pos - 1] !== '*');
-
- // Eat quoted strings
- } else if ((content[pos] === '"' || content[pos] === "'" || content[pos] === "`") &&
- (pos === 0 || content[pos - 1] !== '/')) {
- stop = content[pos];
- do {
- if (content[pos] === '\\') {
- pos += 1;
- }
- pos += 1;
- } while (pos < content.length && content[pos] !== stop);
-
- // Otherwise, check for block level.
- } else if (content[pos] === '{') {
- level += 1;
- } else if (content[pos] === '}') {
- level -= 1;
- if (level === 0) {
- return content.slice(start, pos + 1);
- }
- }
- pos += 1;
- }
- content = content.slice(start + 1);
- }
- return null;
-}
-
-// Stupid function extractor for array.
-function extractArrayVariable(content, arrayName, kind) {
- if (typeof kind === "undefined") {
- kind = "let ";
- }
- var splitter = kind + arrayName;
- while (true) {
- var start = content.indexOf(splitter);
- if (start === -1) {
- break;
- }
- var pos = getNextStep(content, start, '=');
- if (pos === null) {
- break;
- } else if (pos < 0) {
- content = content.slice(-pos);
- continue;
- }
- pos = getNextStep(content, pos, '[');
- if (pos === null) {
- break;
- } else if (pos < 0) {
- content = content.slice(-pos);
- continue;
- }
- while (pos < content.length) {
- if (content[pos] === '"' || content[pos] === "'") {
- var stop = content[pos];
- do {
- if (content[pos] === '\\') {
- pos += 2;
- } else {
- pos += 1;
- }
- } while (pos < content.length &&
- (content[pos] !== stop || content[pos - 1] === '\\'));
- } else if (content[pos] === ']' &&
- pos + 1 < content.length &&
- content[pos + 1] === ';') {
- return content.slice(start, pos + 2);
- }
- pos += 1;
- }
- content = content.slice(start + 1);
- }
- if (kind === "let ") {
- return extractArrayVariable(content, arrayName, "const ");
- }
- return null;
-}
-
-// Stupid function extractor for variable.
-function extractVariable(content, varName, kind) {
- if (typeof kind === "undefined") {
- kind = "let ";
- }
- var splitter = kind + varName;
- while (true) {
- var start = content.indexOf(splitter);
- if (start === -1) {
- break;
- }
- var pos = getNextStep(content, start, '=');
- if (pos === null) {
- break;
- } else if (pos < 0) {
- content = content.slice(-pos);
- continue;
- }
- while (pos < content.length) {
- if (content[pos] === '"' || content[pos] === "'") {
- var stop = content[pos];
- do {
- if (content[pos] === '\\') {
- pos += 2;
- } else {
- pos += 1;
- }
- } while (pos < content.length &&
- (content[pos] !== stop || content[pos - 1] === '\\'));
- } else if (content[pos] === ';' || content[pos] === ',') {
- return content.slice(start, pos + 1);
- }
- pos += 1;
- }
- content = content.slice(start + 1);
- }
- if (kind === "let ") {
- return extractVariable(content, varName, "const ");
- }
- return null;
-}
-
function loadContent(content) {
var Module = module.constructor;
var m = new Module();
return fs.readFileSync(filePath, 'utf8');
}
-function loadThings(thingsToLoad, kindOfLoad, funcToCall, fileContent) {
- var content = '';
- for (var i = 0; i < thingsToLoad.length; ++i) {
- var tmp = funcToCall(fileContent, thingsToLoad[i]);
- if (tmp === null) {
- console.log('unable to find ' + kindOfLoad + ' "' + thingsToLoad[i] + '"');
- process.exit(1);
- }
- content += tmp;
- content += 'exports.' + thingsToLoad[i] + ' = ' + thingsToLoad[i] + ';';
- }
- return content;
-}
-
function contentToDiffLine(key, value) {
return `"${key}": "${value}",`;
}
return null;
}
-function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) {
- if (searchIndex[searchIndex.length - 1].length === 0) {
- searchIndex.pop();
- }
- searchIndex.pop();
- var fullSearchIndex = searchIndex.join("\n") + '\nexports.rawSearchIndex = searchIndex;';
- searchIndex = loadContent(fullSearchIndex);
- var finalJS = "";
-
- var arraysToLoad = ["itemTypes"];
- var variablesToLoad = ["MAX_LEV_DISTANCE", "MAX_RESULTS", "NO_TYPE_FILTER",
- "GENERICS_DATA", "NAME", "INPUTS_DATA", "OUTPUT_DATA",
- "TY_PRIMITIVE", "TY_KEYWORD",
- "levenshtein_row2"];
- // execQuery first parameter is built in getQuery (which takes in the search input).
- // execQuery last parameter is built in buildIndex.
- // buildIndex requires the hashmap from search-index.
- var functionsToLoad = ["buildHrefAndPath", "pathSplitter", "levenshtein", "validateResult",
- "buildIndex", "execQuery", "parseQuery", "createQueryResults",
- "isWhitespace", "isSpecialStartCharacter", "isStopCharacter",
- "parseInput", "getItemsBefore", "getNextElem", "createQueryElement",
- "isReturnArrow", "isPathStart", "getStringElem", "newParsedQuery",
- "itemTypeFromName", "isEndCharacter", "isErrorCharacter",
- "isIdentCharacter", "isSeparatorCharacter", "getIdentEndPosition",
- "checkExtraTypeFilterCharacters", "isWhitespaceCharacter"];
-
- const functions = ["hasOwnPropertyRustdoc", "onEach"];
- ALIASES = {};
- finalJS += 'window = { "currentCrate": "' + crate + '", rootPath: "../" };\n';
- finalJS += loadThings(functions, 'function', extractFunction, storageJs);
- finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, searchJs);
- finalJS += loadThings(variablesToLoad, 'variable', extractVariable, searchJs);
- finalJS += loadThings(functionsToLoad, 'function', extractFunction, searchJs);
-
- var loaded = loadContent(finalJS);
- var index = loaded.buildIndex(searchIndex.rawSearchIndex);
-
- return [loaded, index];
-}
-
// This function checks if `expected` has all the required fields needed for the checks.
function checkNeededFields(fullPath, expected, error_text, queryName, position) {
let fieldsToCheck;
'compared to EXPECTED');
}
} else if (expected !== null && typeof expected !== "undefined" &&
- expected.constructor == Object)
- {
+ expected.constructor == Object) {
for (const key in expected) {
if (!expected.hasOwnProperty(key)) {
continue;
}
}
-function runParser(query, expected, loaded, loadedFile, queryName) {
+function runParser(query, expected, parseQuery, queryName) {
var error_text = [];
checkNeededFields("", expected, error_text, queryName, null);
if (error_text.length === 0) {
- valueCheck('', expected, loaded.parseQuery(query), error_text, queryName);
+ valueCheck('', expected, parseQuery(query), error_text, queryName);
}
return error_text;
}
-function runSearch(query, expected, index, loaded, loadedFile, queryName) {
- const filter_crate = loadedFile.FILTER_CRATE;
+function runSearch(query, expected, doSearch, loadedFile, queryName) {
const ignore_order = loadedFile.ignore_order;
const exact_check = loadedFile.exact_check;
- var results = loaded.execQuery(loaded.parseQuery(query), index, filter_crate);
+ var results = doSearch(query, loadedFile.FILTER_CRATE);
var error_text = [];
for (var key in expected) {
return 0;
}
-function runChecks(testFile, loaded, index) {
+function runChecks(testFile, doSearch, parseQuery) {
var checkExpected = false;
var checkParsed = false;
var testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;';
if (checkExpected) {
res += runCheck(loadedFile, "EXPECTED", (query, expected, text) => {
- return runSearch(query, expected, index, loaded, loadedFile, text);
+ return runSearch(query, expected, doSearch, loadedFile, text);
});
}
if (checkParsed) {
res += runCheck(loadedFile, "PARSED", (query, expected, text) => {
- return runParser(query, expected, loaded, loadedFile, text);
+ return runParser(query, expected, parseQuery, text);
});
}
return res;
}
-function load_files(doc_folder, resource_suffix, crate) {
- var searchJs = readFile(path.join(doc_folder, "search" + resource_suffix + ".js"));
- var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js"));
- var searchIndex = readFile(
- path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n");
-
- return loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate);
+/**
+ * Load searchNNN.js and search-indexNNN.js.
+ *
+ * @param {string} doc_folder - Path to a folder generated by running rustdoc
+ * @param {string} resource_suffix - Version number between filename and .js, e.g. "1.59.0"
+ * @returns {Object} - Object containing two keys: `doSearch`, which runs a search
+ * with the loaded index and returns a table of results; and `parseQuery`, which is the
+ * `parseQuery` function exported from the search module.
+ */
+function loadSearchJS(doc_folder, resource_suffix) {
+ const searchJs = path.join(doc_folder, "search" + resource_suffix + ".js");
+ const searchIndexJs = path.join(doc_folder, "search-index" + resource_suffix + ".js");
+ const searchIndex = require(searchIndexJs);
+ const searchModule = require(searchJs);
+ const searchWords = searchModule.initSearch(searchIndex.searchIndex);
+
+ return {
+ doSearch: function (queryStr, filterCrate, currentCrate) {
+ return searchModule.execQuery(searchModule.parseQuery(queryStr), searchWords,
+ filterCrate, currentCrate);
+ },
+ parseQuery: searchModule.parseQuery,
+ }
}
function showHelp() {
return null;
}
-function checkFile(test_file, opts, loaded, index) {
- const test_name = path.basename(test_file, ".js");
-
- process.stdout.write('Checking "' + test_name + '" ... ');
- return runChecks(test_file, loaded, index);
-}
-
function main(argv) {
var opts = parseOptions(argv.slice(2));
if (opts === null) {
return 1;
}
- var [loaded, index] = load_files(
+ let parseAndSearch = loadSearchJS(
opts["doc_folder"],
- opts["resource_suffix"],
- opts["crate_name"]);
+ opts["resource_suffix"]);
var errors = 0;
+ let doSearch = function (queryStr, filterCrate) {
+ return parseAndSearch.doSearch(queryStr, filterCrate, opts["crate_name"]);
+ };
+
if (opts["test_file"].length !== 0) {
- opts["test_file"].forEach(function(file) {
- errors += checkFile(file, opts, loaded, index);
+ opts["test_file"].forEach(function (file) {
+ process.stdout.write(`Testing ${file} ... `);
+ errors += runChecks(file, doSearch, parseAndSearch.parseQuery);
});
} else if (opts["test_folder"].length !== 0) {
- fs.readdirSync(opts["test_folder"]).forEach(function(file) {
+ fs.readdirSync(opts["test_folder"]).forEach(function (file) {
if (!file.endsWith(".js")) {
return;
}
- errors += checkFile(path.join(opts["test_folder"], file), opts, loaded, index);
+ process.stdout.write(`Testing ${file} ... `);
+ errors += runChecks(path.join(opts["test_folder"], file), doSearch,
+ parseAndSearch.parseQuery);
});
}
return errors > 0 ? 1 : 0;