Commit b037845a authored by Diegodlh's avatar Diegodlh
Browse files

Add Match transformation support

parent cca6f19a
......@@ -5,6 +5,7 @@ import {
SplitTransformation,
MatchTransformation,
TransformationConfigTypeError,
Transformation,
} from "./transformation";
describe("Join tranformation", () => {
......@@ -223,8 +224,11 @@ describe("Range transformation", () => {
describe("Match transformation", () => {
it("extracts single occurrence of target substring", async () => {
const transformation = new MatchTransformation();
transformation.config = "matching";
const transformation = Transformation.create({
type: "match",
itemwise: true,
config: "substring",
});
const input = ["a substring inside a string"];
expect(await transformation.transform(input)).toEqual(["substring"]);
});
......@@ -278,19 +282,35 @@ describe("Match transformation", () => {
});
it("supports regular expressions between //, including flags", async () => {
const transformation = new MatchTransformation(false);
transformation.config = "/(sub)?string/i";
const transformation = new MatchTransformation();
transformation.config = "/(?:sub)?string/i";
const input = ["a Substring inside a string"];
expect(await transformation.transform(input)).toEqual(["Substring"]);
});
it("returns first regex match only, unless global flag is set", async () => {
const transformation = new MatchTransformation();
transformation.config = "/(?:sub)?string/i";
const input = ["a Substring inside a string"];
expect(await transformation.transform(input)).toEqual(["Substring"]);
transformation.config = "/(?:sub)?string/ig";
expect(await transformation.transform(input)).toEqual([
"Substring",
"string",
]);
});
it("accepts optional double quotes to force plain string matching", async () => {
const transformation = new MatchTransformation(false);
transformation.config = '"/(sub)?string/i"';
const input = ["a Substring inside a string", "/(sub)?string/i"];
expect(await transformation.transform(input)).toEqual(["/(sub)?string/i"]);
it("supports regex capturing groups", async () => {
const transformation = new MatchTransformation();
transformation.config = "/(sub)string/i";
const input = ["a Substring inside a string"];
expect(await transformation.transform(input)).toEqual(["Substring", "Sub"]);
});
it("rejects invalid regular expressions", async () => {
const transformation = new MatchTransformation();
expect(() => {
transformation.config = "/+/";
}).toThrow();
});
});
......@@ -43,6 +43,9 @@ export abstract class Transformation extends TranslationStep {
case "range":
return new RangeTransformation(itemwise, config);
break;
case "match":
return new MatchTransformation(itemwise, config);
break;
default:
throw new Error(
`Unknown transformation of type ${transformation.type}`
......@@ -257,7 +260,57 @@ type Range = {
};
/** Regular expression transformation step class. */
// class MatchTransformation extends Transformation {}
export class MatchTransformation extends Transformation {
private _target!: RegExp;
constructor(itemwise = true, target = "/.*/") {
super("match", itemwise);
this.config = target;
}
set config(config: string) {
let regex, flags;
const match = config.match(/^\/(?<regex>.*)\/(?<flags>[a-z]+)?$/);
if (match === null) {
// string matching defaults to global matching
this._target = new RegExp(config, "g");
this._config = config;
} else {
({ regex, flags } = match.groups ?? {});
if (regex === undefined) {
// the "regex" capturing group can't be undefined if match isn't null
throw new Error("Unexpected undefined regex capturing group");
} else {
try {
this._target = new RegExp(regex, flags);
this._config = config;
} catch (error) {
let info;
if (error instanceof Error) info = error.message;
// config interpreted as regular expression, but failed to parse it
throw new TransformationConfigTypeError("match", config, info);
}
}
}
}
get config(): string {
return this._config;
}
transform(input: StepOutput): Promise<StepOutput> {
if (!this.itemwise) {
input = [input.join()];
}
const output: StepOutput = input.reduce((matches: StepOutput, item) => {
const match = item.match(this._target);
if (match !== null) {
matches = matches.concat(match);
}
return matches;
}, []);
return Promise.resolve(output);
}
}
// class ReplaceTransformation extends Transformation {}
......@@ -271,9 +324,14 @@ type Range = {
// }
export class TransformationConfigTypeError extends TypeError {
constructor(transformationType: TransformationType, config: string) {
constructor(
transformationType: TransformationType,
config: string,
info?: string
) {
super(
`"${config}" is not a valid configuration value for tranformation type "${transformationType}"`
);
if (info) this.message += `: ${info}`;
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment