Skip to content

Commit

Permalink
fix(encoding/toml): parse keys correctly (#1019)
Browse files Browse the repository at this point in the history
  • Loading branch information
FujiHaruka authored Jul 13, 2021
1 parent 6649e0a commit bac1c3e
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 26 deletions.
12 changes: 12 additions & 0 deletions encoding/testdata/keys.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"" = 1
"127.0.0.1" = 1
"ʎǝʞ" = 1
'this is "literal"' = 1
"double \"quote\"" = 1

site."google.com".bar = 1
site."google.com".baz = 1

a . b . c = 1
a . b . d = 1
a . e = 1
82 changes: 56 additions & 26 deletions encoding/toml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,34 +445,48 @@ class Parser {
_parseDeclarationName(declaration: string): string[] {
const out = [];
let acc = [];
let inBasicString = false;
let inLiteral = false;
for (let i = 0; i < declaration.length; i++) {
const c = declaration[i];
const inString = inBasicString || inLiteral;
switch (c) {
case ".":
if (!inLiteral) {
if (!inString) {
out.push(acc.join(""));
acc = [];
} else {
acc.push(c);
}
break;
case `"`:
if (inLiteral) {
inLiteral = false;
} else {
if (!inString) {
inBasicString = true;
} else if (inBasicString && declaration[i - 1] !== "\\") {
inBasicString = false;
}
acc.push(c);
break;
case "'":
if (!inString) {
inLiteral = true;
} else if (inLiteral) {
inLiteral = false;
}
acc.push(c);
break;
default:
acc.push(c);
break;
}
}
if (inBasicString || inLiteral) {
throw new TOMLError(`Invalid key: ${declaration}`);
}
if (acc.length !== 0) {
out.push(acc.join(""));
}
return out;
return out.map((name) => trim(name));
}
_parseLines(): void {
for (let i = 0; i < this.tomlLines.length; i++) {
Expand Down Expand Up @@ -523,30 +537,46 @@ class Parser {
}
}
_cleanOutput(): void {
this._propertyClean(this.context.output);
this._unflatProperties(this.context.output);
this._cleanKeys(this.context.output);
}
_propertyClean(obj: Record<string, unknown>): void {
_unflatProperties(obj: Record<string, unknown>): void {
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
let k = keys[i];
if (k) {
let v = obj[k];
const pathDeclaration = this._parseDeclarationName(k);
delete obj[k];
if (pathDeclaration.length > 1) {
const shift = pathDeclaration.shift();
if (shift) {
k = shift.replace(/"/g, "");
v = this._unflat(pathDeclaration, v as Record<string, unknown>);
}
} else {
k = k.replace(/"/g, "");
}
obj[k] = v;
if (v instanceof Object) {
// deno-lint-ignore no-explicit-any
this._propertyClean(v as any);
for (const k of keys) {
let v = obj[k];
const pathDeclaration = this._parseDeclarationName(k);
const first = pathDeclaration.shift()!;
if (first !== k) {
if (pathDeclaration.length > 0) {
const flatten = this._unflat(
pathDeclaration,
v as Record<string, unknown>,
);
const current = obj[first] instanceof Object ? obj[first] : {};
v = deepAssign(
current,
flatten,
);
}
delete obj[k];
obj[first] = v;
}
if (v instanceof Object) {
this._unflatProperties(v as Record<string, unknown>);
}
}
}
_cleanKeys(obj: Record<string, unknown>): void {
const keys = Object.keys(obj);
for (const k of keys) {
const v = obj[k];
if (k.startsWith('"') || k.startsWith("'")) {
const parsed = this._parseString(k);
delete obj[k];
obj[parsed] = v;
}
if (v instanceof Object) {
this._cleanKeys(v as Record<string, unknown>);
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions encoding/toml_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,23 @@ Deno.test({
},
});

Deno.test({
name: "[TOML] Various keys",
fn(): void {
const expected = {
site: { "google.com": { bar: 1, baz: 1 } },
a: { b: { c: 1, d: 1 }, e: 1 },
"": 1,
"127.0.0.1": 1,
"ʎǝʞ": 1,
'this is "literal"': 1,
'double "quote"': 1,
};
const actual = parseFile(path.join(testdataDir, "keys.toml"));
assertEquals(actual, expected);
},
});

Deno.test({
name: "[TOML] Simple",
fn(): void {
Expand Down

0 comments on commit bac1c3e

Please sign in to comment.