Skip to content

Commit

Permalink
[feat] added a way to parse env strings
Browse files Browse the repository at this point in the history
  • Loading branch information
silentrald committed Jan 20, 2025
1 parent effe060 commit 3046519
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 1 deletion.
61 changes: 61 additions & 0 deletions src/__tests__/unit/env.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { parseEnvString } from "main/helpers/env.helpers";

describe("Test parseEnvString", () => {

it("Empty", () => {
const envVars = parseEnvString("");
expect(envVars).toEqual({});
});

it("Single test; no quotes", () => {
const envString = "HELLO=World!";
const envVars = parseEnvString(envString);
expect(envVars).toEqual({
HELLO: "World!",
});
});

it("Single test; single quotes", () => {
const envString = "SINGLE_QOUTE='Single quote with spaces'";
const envVars = parseEnvString(envString);
expect(envVars).toEqual({
SINGLE_QOUTE: "Single quote with spaces",
});
});

it("Single test; double quotes", () => {
const envString = 'DOUBLE_QOUTE="Some random quote."';
const envVars = parseEnvString(envString);
expect(envVars).toEqual({
DOUBLE_QOUTE: "Some random quote.",
});
});

it("Single test; empty value", () => {
const envString = "EMPTY=";
const envVars = parseEnvString(envString);
expect(envVars).toEqual({
EMPTY: "",
});
});

it("Multiple test; combined", () => {
const envString = `HELLO=World! DOUBLE_QUOTE="Two Words" SINGLE_QUOTE='' EMPTY=`
const envVars = parseEnvString(envString);
expect(envVars).toEqual(expect.objectContaining({
HELLO: "World!",
DOUBLE_QUOTE: "Two Words",
SINGLE_QUOTE: "",
EMPTY: ""
}));
});

it("Key with numbers and lower case", () => {
const envString = "H3ll0=world";
const envVars = parseEnvString(envString);
expect(envVars).toEqual({
H3ll0: "world",
});
});

});
117 changes: 116 additions & 1 deletion src/main/helpers/env.helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CustomError } from "shared/models/exceptions/custom-error.class";
import { ProviderPlatform } from "shared/models/provider-platform.enum";

export function execOnOs<T>(executions: { [key in ProviderPlatform]?: () => T }, noError = false): T {
Expand All @@ -10,4 +11,118 @@ export function execOnOs<T>(executions: { [key in ProviderPlatform]?: () => T },
}

return undefined;
}
}

enum EnvParserState {
NAME_START,
NAME,
VALUE_START,
VALUE,
QUOTE_VALUE,
DQUOTE_VALUE,
SPACE,
ERROR,
};

const isAlphaCharacter = (c: string) =>
(c >= "a" && c <= "z") || (c >= "A" && c <= "Z");
const isNumber = (c: string) => c >= "0" && c <= "9";

export function parseEnvString(envString: string): Record<string, string> {
const envVars: Record<string, string> = {};

let state: EnvParserState = EnvParserState.NAME_START;
let index = 0;
let newName = "";
for (let pos = 0; pos < envString.length; ++pos) {
const c = envString[pos];

switch (state) {
case EnvParserState.NAME_START:
if (isAlphaCharacter(c) || c === "_") {
state = EnvParserState.NAME;
index = pos;
} else if (c !== " ") {
state = EnvParserState.ERROR;
}
break;

case EnvParserState.NAME:
if (c === "=") {
state = EnvParserState.VALUE_START;
newName = envString.substring(index, pos);
index = pos + 1;
} else if (!isAlphaCharacter(c) && !isNumber(c) && c !== "_") {
state = EnvParserState.ERROR;
}
break;

case EnvParserState.VALUE_START:
if (c === "'") {
++index;
state = EnvParserState.QUOTE_VALUE;
} else if (c === '"') {
++index;
state = EnvParserState.DQUOTE_VALUE;
} else if (c === " ") {
state = EnvParserState.NAME_START;
envVars[newName] = "";
} else {
state = EnvParserState.VALUE;
}
break;

case EnvParserState.VALUE:
if (c === " ") {
state = EnvParserState.NAME_START;
envVars[newName] = envString.substring(index, pos);
}
break;

case EnvParserState.QUOTE_VALUE:
if (c === "'") {
state = EnvParserState.SPACE;
envVars[newName] = envString.substring(index, pos);
}
break;

case EnvParserState.DQUOTE_VALUE:
if (c === '"') {
state = EnvParserState.SPACE;
envVars[newName] = envString.substring(index, pos);
}
break;

case EnvParserState.SPACE:
if (c === " ") {
state = EnvParserState.NAME_START;
} else {
state = EnvParserState.ERROR;
}
break;

default:
}

if (state === EnvParserState.ERROR) {
throw new CustomError(
`parseEnvString failed: invalid character at position ${pos}`,
"env.parse"
);
}
}

if (state === EnvParserState.VALUE_START || state === EnvParserState.VALUE) {
envVars[newName] = envString.substring(index);
return envVars;
}

if (state === EnvParserState.NAME_START || state === EnvParserState.SPACE) {
return envVars;
}

throw new CustomError(
"parseEnvString failed: invalid ending state",
"env.parse"
);
}

0 comments on commit 3046519

Please sign in to comment.