Rename folders
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import {Array, BooleanData, ExpressionData, Kind} from "../data";
|
||||
import {equals} from "../result";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const contains: FunctionDefinition = {
|
||||
name: "contains",
|
||||
description:
|
||||
"`contains( search, item )`\n\nReturns `true` if `search` contains `item`. If `search` is an array, this function returns `true` if the `item` is an element in the array. If `search` is a string, this function returns `true` if the `item` is a substring of `search`. This function is not case sensitive. Casts values to a string.",
|
||||
minArgs: 2,
|
||||
maxArgs: 2,
|
||||
call: (...args: ExpressionData[]): ExpressionData => {
|
||||
const left = args[0];
|
||||
const right = args[1];
|
||||
|
||||
if (left.primitive) {
|
||||
const ls = left.coerceString();
|
||||
if (right.primitive) {
|
||||
const rs = right.coerceString();
|
||||
return new BooleanData(ls.toLowerCase().includes(rs.toLowerCase()));
|
||||
}
|
||||
} else if (left.kind === Kind.Array) {
|
||||
const la = left as Array;
|
||||
if (la.values().length === 0) {
|
||||
return new BooleanData(false);
|
||||
}
|
||||
|
||||
for (const v of la.values()) {
|
||||
if (equals(right, v)) {
|
||||
return new BooleanData(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new BooleanData(false);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
import {BooleanData, ExpressionData} from "../data";
|
||||
import {toUpperSpecial} from "../result";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const endswith: FunctionDefinition = {
|
||||
name: "endsWith",
|
||||
description:
|
||||
"`endsWith( searchString, searchValue )`\n\nReturns `true` if `searchString` ends with `searchValue`. This function is not case sensitive. Casts values to a string.",
|
||||
minArgs: 2,
|
||||
maxArgs: 2,
|
||||
call: (...args: ExpressionData[]): ExpressionData => {
|
||||
const left = args[0];
|
||||
if (!left.primitive) {
|
||||
return new BooleanData(false);
|
||||
}
|
||||
|
||||
const right = args[1];
|
||||
if (!right.primitive) {
|
||||
return new BooleanData(false);
|
||||
}
|
||||
|
||||
const ls = toUpperSpecial(left.coerceString());
|
||||
const rs = toUpperSpecial(right.coerceString());
|
||||
|
||||
return new BooleanData(ls.endsWith(rs));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import {Null, NumberData, StringData} from "../data";
|
||||
import {format} from "./format";
|
||||
|
||||
describe("format", () => {
|
||||
it("null", () => {
|
||||
expect(format.call(new StringData("{0}"), new Null())).toEqual(new StringData(""));
|
||||
});
|
||||
it("number", () => {
|
||||
expect(format.call(new StringData("{0}"), new NumberData(42))).toEqual(new StringData("42"));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,120 @@
|
||||
import {ExpressionData, StringData} from "../data";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const format: FunctionDefinition = {
|
||||
name: "format",
|
||||
description:
|
||||
"`format( string, replaceValue0, replaceValue1, ..., replaceValueN)`\n\nReplaces values in the `string`, with the variable `replaceValueN`. Variables in the `string` are specified using the `{N}` syntax, where `N` is an integer. You must specify at least one `replaceValue` and `string`. There is no maximum for the number of variables (`replaceValueN`) you can use. Escape curly braces using double braces.",
|
||||
minArgs: 1,
|
||||
maxArgs: 255 /*MAX_ARGUMENTS*/,
|
||||
call: (...args: ExpressionData[]): ExpressionData => {
|
||||
const fs = args[0].coerceString();
|
||||
|
||||
const result: string[] = [];
|
||||
let index = 0;
|
||||
|
||||
while (index < fs.length) {
|
||||
const lbrace = fs.indexOf("{", index);
|
||||
let rbrace = fs.indexOf("}", index);
|
||||
|
||||
// Left brace
|
||||
if (lbrace >= 0 && (rbrace < 0 || rbrace > lbrace)) {
|
||||
// Escaped left brace
|
||||
if (safeCharAt(fs, lbrace + 1) === "{") {
|
||||
result.push(fs.substr(index, lbrace - index + 1));
|
||||
index = lbrace + 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Left brace, number, optional format specifiers, right brace
|
||||
if (rbrace > lbrace + 1) {
|
||||
const argIndex = readArgIndex(fs, lbrace + 1);
|
||||
if (argIndex.success) {
|
||||
// Check parameter count
|
||||
if (1 + argIndex.result > args.length - 1) {
|
||||
throw new Error(`The following format string references more arguments than were supplied: ${fs}`);
|
||||
}
|
||||
|
||||
// Append the portion before the left brace
|
||||
if (lbrace > index) {
|
||||
result.push(fs.substr(index, lbrace - index));
|
||||
}
|
||||
|
||||
// Append the arg
|
||||
result.push(`${args[1 + argIndex.result].coerceString()}`);
|
||||
index = rbrace + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`The following format string is invalid: ${fs}`);
|
||||
}
|
||||
// Right brace
|
||||
else if (rbrace >= 0) {
|
||||
// Escaped right brace
|
||||
if (safeCharAt(fs, rbrace + 1) === "}") {
|
||||
result.push(fs.substr(index, rbrace - index + 1));
|
||||
index = rbrace + 2;
|
||||
} else {
|
||||
throw new Error(`The following format string is invalid: ${fs}`);
|
||||
}
|
||||
}
|
||||
// Last segment
|
||||
else {
|
||||
result.push(fs.substr(index));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new StringData(result.join(""));
|
||||
}
|
||||
};
|
||||
|
||||
function safeCharAt(string: string, index: number): string {
|
||||
if (string.length > index) {
|
||||
return string[index];
|
||||
}
|
||||
|
||||
return "\0";
|
||||
}
|
||||
|
||||
function readArgIndex(string: string, startIndex: number): ArgIndex {
|
||||
// Count the number of digits
|
||||
let length = 0;
|
||||
while (true) {
|
||||
const nextChar = safeCharAt(string, startIndex + length);
|
||||
if (nextChar >= "0" && nextChar <= "9") {
|
||||
length++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate at least one digit
|
||||
if (length < 1) {
|
||||
return <ArgIndex>{
|
||||
success: false
|
||||
};
|
||||
}
|
||||
|
||||
// Parse the number
|
||||
const endIndex = startIndex + length - 1;
|
||||
const result = parseInt(string.substr(startIndex, length));
|
||||
return <ArgIndex>{
|
||||
success: !isNaN(result),
|
||||
result: result,
|
||||
endIndex: endIndex
|
||||
};
|
||||
}
|
||||
|
||||
interface ArgIndex {
|
||||
success: boolean;
|
||||
result: number;
|
||||
endIndex: number;
|
||||
}
|
||||
|
||||
interface FormatSpecifiers {
|
||||
success: boolean;
|
||||
result: string;
|
||||
rbrace: number;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import {ExpressionData} from "../data";
|
||||
import {reviver} from "../data/reviver";
|
||||
import {ExpressionEvaluationError} from "../errors";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const fromjson: FunctionDefinition = {
|
||||
name: "fromJson",
|
||||
description:
|
||||
"`fromJSON(value)`\n\nReturns a JSON object or JSON data type for `value`. You can use this function to provide a JSON object as an evaluated expression or to convert environment variables from a string.",
|
||||
minArgs: 1,
|
||||
maxArgs: 1,
|
||||
call: (...args: ExpressionData[]): ExpressionData => {
|
||||
const input = args[0];
|
||||
const is = input.coerceString();
|
||||
|
||||
if (is.trim() === "") {
|
||||
throw new Error("empty input");
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(is, reviver);
|
||||
} catch (e) {
|
||||
throw new ExpressionEvaluationError("Error parsing JSON when evaluating fromJson", {cause: e});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import {ExpressionData} from "../data";
|
||||
|
||||
export interface FunctionInfo {
|
||||
name: string;
|
||||
|
||||
description?: string;
|
||||
|
||||
minArgs: number;
|
||||
maxArgs: number;
|
||||
}
|
||||
|
||||
export interface FunctionDefinition extends FunctionInfo {
|
||||
call: (...args: ExpressionData[]) => ExpressionData;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import {Array, ExpressionData, Kind, StringData} from "../data";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const join: FunctionDefinition = {
|
||||
name: "join",
|
||||
description:
|
||||
"`join( array, optionalSeparator )`\n\nThe value for `array` can be an array or a string. All values in `array` are concatenated into a string. If you provide `optionalSeparator`, it is inserted between the concatenated values. Otherwise, the default separator `,` is used. Casts values to a string.",
|
||||
minArgs: 1,
|
||||
maxArgs: 2,
|
||||
call: (...args: ExpressionData[]): ExpressionData => {
|
||||
// Primitive
|
||||
if (args[0].primitive) {
|
||||
return new StringData(args[0].coerceString());
|
||||
}
|
||||
|
||||
// Array
|
||||
if (args[0].kind === Kind.Array) {
|
||||
// Separator
|
||||
let separator = ",";
|
||||
if (args.length > 1 && args[1].primitive) {
|
||||
separator = args[1].coerceString();
|
||||
}
|
||||
|
||||
// Convert items to strings
|
||||
return new StringData(
|
||||
(args[0] as Array)
|
||||
.values()
|
||||
.map(item => item.coerceString())
|
||||
.join(separator)
|
||||
);
|
||||
}
|
||||
|
||||
return new StringData("");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
import {BooleanData, ExpressionData} from "../data";
|
||||
import {toUpperSpecial} from "../result";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const startswith: FunctionDefinition = {
|
||||
name: "startsWith",
|
||||
description:
|
||||
"`startsWith( searchString, searchValue )`\n\nReturns `true` when `searchString` starts with `searchValue`. This function is not case sensitive. Casts values to a string.",
|
||||
minArgs: 2,
|
||||
maxArgs: 2,
|
||||
call: (...args: ExpressionData[]): ExpressionData => {
|
||||
const left = args[0];
|
||||
if (!left.primitive) {
|
||||
return new BooleanData(false);
|
||||
}
|
||||
|
||||
const right = args[1];
|
||||
if (!right.primitive) {
|
||||
return new BooleanData(false);
|
||||
}
|
||||
|
||||
const ls = toUpperSpecial(left.coerceString());
|
||||
const rs = toUpperSpecial(right.coerceString());
|
||||
|
||||
return new BooleanData(ls.startsWith(rs));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import {ExpressionData, StringData} from "../data";
|
||||
import {replacer} from "../data/replacer";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const tojson: FunctionDefinition = {
|
||||
name: "toJson",
|
||||
description:
|
||||
"`toJSON(value)`\n\nReturns a pretty-print JSON representation of `value`. You can use this function to debug the information provided in contexts.",
|
||||
minArgs: 1,
|
||||
maxArgs: 1,
|
||||
call: (...args: ExpressionData[]): ExpressionData => {
|
||||
return new StringData(JSON.stringify(args[0], replacer, " "));
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user