feat: Add string insertion operators Append, Prepend, and Insert At

- Added Append operator to insert text at the end of the input
- Added Prepend operator to insert text at the start of the input
- Added Insert At operator to insert text at a given index within the input
- Added tests for all new operations.
This commit is contained in:
Oliver Mitchell 2026-05-06 02:53:23 +09:30
parent ec4f1f5605
commit 861313e493
7 changed files with 370 additions and 1 deletions

View File

@ -349,7 +349,10 @@
"Sleep",
"File Tree",
"Take nth bytes",
"Drop nth bytes"
"Drop nth bytes",
"Append",
"Prepend",
"Insert At"
]
},
{

View File

@ -0,0 +1,64 @@
/**
* @author oliver-mitchell [oliver@polymerlabs.dev]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import { DELIM_OPTIONS } from "../lib/Delim.mjs";
/**
* Append operation.
*/
class Append extends Operation {
/**
* Append constructor.
*/
constructor() {
super();
this.name = "Append";
this.module = "Default";
this.description = "Add characters to the end of the input string, optionally with respect to a delimeter.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Text",
"type": "binaryString",
"value": ""
},
{
"name": "Delimiter",
"type": "option",
"value": ["None", ...DELIM_OPTIONS]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [text, delim] = args;
if (delim === "None") {
return input + text;
}
const output = [],
delimChar = Utils.charRep(delim),
subs = input.split(Utils.charRep(delim));
for (let i = 0; i < subs.length; i++) {
output.push(subs[i] + text);
}
return output.join(delimChar);
}
}
export default Append;

View File

@ -0,0 +1,88 @@
/**
* @author oliver-mitchell [oliver@polymerlabs.dev]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import { DELIM_OPTIONS } from "../lib/Delim.mjs";
/**
* Insert At operation.
*/
class InsertAt extends Operation {
/**
* InsertAt constructor.
*/
constructor() {
super();
this.name = "Insert At";
this.module = "Default";
this.description = "Add characters to input string at a given index, optionally with respect to a delimeter.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Text",
"type": "binaryString",
"value": ""
},
{
"name": "Index",
"type": "number",
"value": 0,
"min": 0
},
{
"name": "Delimiter",
"type": "option",
"value": ["None", ...DELIM_OPTIONS]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [text, index, delim] = args;
if (delim === "None") {
return InsertAt.performInsert(input, text, index);
}
const output = [],
delimChar = Utils.charRep(delim),
subs = input.split(Utils.charRep(delim));
for (let i = 0; i < subs.length; i++) {
output.push(InsertAt.performInsert(subs[i], text, index));
}
return output.join(delimChar);
}
/**
* @param {string} input
* @param {string} text
* @param {number} index
* @returns {string}
*/
static performInsert(input, text, index) {
if (index >= input.length) {
return input + text;
}
if (index <= 0) {
return text + input;
}
return input.slice(0, index) + text + input.slice(index);
}
}
export default InsertAt;

View File

@ -0,0 +1,64 @@
/**
* @author oliver-mitchell [oliver@polymerlabs.dev]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import { DELIM_OPTIONS } from "../lib/Delim.mjs";
/**
* Prepend operation.
*/
class Prepend extends Operation {
/**
* Prepend constructor.
*/
constructor() {
super();
this.name = "Prepend";
this.module = "Default";
this.description = "Add characters to the start of the input string, optionally with respect to a delimeter.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Text",
"type": "binaryString",
"value": ""
},
{
"name": "Delimiter",
"type": "option",
"value": ["None", ...DELIM_OPTIONS]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [text, delim] = args;
if (delim === "None") {
return input + text;
}
const output = [],
delimChar = Utils.charRep(delim),
subs = input.split(Utils.charRep(delim));
for (let i = 0; i < subs.length; i++) {
output.push(text + subs[i]);
}
return output.join(delimChar);
}
}
export default Prepend;

View File

@ -0,0 +1,42 @@
/**
* Media operation tests.
* @author oliver-mitchell [oliver@polymerlabs.dev]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Append empty",
input: "test",
expectedOutput: "test",
recipeConfig: [
{ op: "Append", args: ["", "None"] } // [text, delimiter]
]
},
{
name: "Append empty input",
input: "",
expectedOutput: "test",
recipeConfig: [
{ op: "Append", args: ["test", "None"] } // [text, delimiter]
]
},
{
name: "Append with input and text",
input: "hello ",
expectedOutput: "hello world",
recipeConfig: [
{ op: "Append", args: ["world", "None"] } // [text, delimiter]
]
},
{
name: "Append with space delim",
input: "hello world",
expectedOutput: "hello: world:",
recipeConfig: [
{ op: "Append", args: [":", "Space"] } // [text, delimiter]
]
}
]);

View File

@ -0,0 +1,66 @@
/**
* Media operation tests.
* @author oliver-mitchell [oliver@polymerlabs.dev]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Insert At empty",
input: "test",
expectedOutput: "test",
recipeConfig: [
{ op: "Insert At", args: ["", 0, "None"] } // [text, index, delimiter]
]
},
{
name: "Insert At empty input",
input: "",
expectedOutput: "test",
recipeConfig: [
{ op: "Insert At", args: ["test", 0, "None"] } // [text, index, delimiter]
]
},
{
name: "Insert At with input and text",
input: "hello ",
expectedOutput: "hello world",
recipeConfig: [
{ op: "Insert At", args: ["world", 6, "None"] } // [text, index, delimiter]
]
},
{
name: "Insert At negative",
input: " world",
expectedOutput: "hello world",
recipeConfig: [
{ op: "Insert At", args: ["hello", -5, "None"] } // [text, index, delimiter]
]
},
{
name: "Insert At beyond string length",
input: "hello ",
expectedOutput: "hello world",
recipeConfig: [
{ op: "Insert At", args: ["world", 100, "None"] } // [text, index, delimiter]
]
},
{
name: "Insert At inside",
input: "heo",
expectedOutput: "hello",
recipeConfig: [
{ op: "Insert At", args: ["ll", 2, "None"] } // [text, index, delimiter]
]
},
{
name: "Insert At with space delim",
input: "hello world",
expectedOutput: "he:llo wo:rld",
recipeConfig: [
{ op: "Insert At", args: [":", 2, "Space"] } // [text, index, delimiter]
]
}
]);

View File

@ -0,0 +1,42 @@
/**
* Media operation tests.
* @author oliver-mitchell [oliver@polymerlabs.dev]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Prepend empty",
input: "test",
expectedOutput: "test",
recipeConfig: [
{ op: "Prepend", args: ["", "None"] } // [text, delimiter]
]
},
{
name: "Prepend empty input",
input: "",
expectedOutput: "test",
recipeConfig: [
{ op: "Prepend", args: ["test", "None"] } // [text, delimiter]
]
},
{
name: "Prepend with input and text",
input: " world",
expectedOutput: "hello world",
recipeConfig: [
{ op: "Prepend", args: ["hello", "None"] } // [text, delimiter]
]
},
{
name: "Prepend with space delim",
input: "hello world",
expectedOutput: ":hello :world",
recipeConfig: [
{ op: "Prepend", args: [":", "Space"] } // [text, delimiter]
]
}
]);