1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-26 16:09:27 -05:00

fix TOML's key encoding (denoland/deno_std#612)

Original: 54a5b95fef
This commit is contained in:
Yusuke Sakurai 2019-09-24 00:08:57 +09:00 committed by Ryan Dahl
parent 9739ba55df
commit aa21e7bc81
2 changed files with 59 additions and 28 deletions

View file

@ -387,6 +387,18 @@ class Parser {
} }
} }
// Bare keys may only contain ASCII letters,
// ASCII digits, underscores, and dashes (A-Za-z0-9_-).
function joinKeys(keys: string[]): string {
// Dotted keys are a sequence of bare or quoted keys joined with a dot.
// This allows for grouping similar properties together:
return keys
.map((str: string): string => {
return str.match(/[^A-Za-z0-9_-]/) ? `"${str}"` : str;
})
.join(".");
}
class Dumper { class Dumper {
maxPad: number = 0; maxPad: number = 0;
srcObject: object; srcObject: object;
@ -400,7 +412,7 @@ class Dumper {
this.output = this._format(); this.output = this._format();
return this.output; return this.output;
} }
_parse(obj: Record<string, unknown>, path: string = ""): string[] { _parse(obj: Record<string, unknown>, keys: string[] = []): string[] {
const out = []; const out = [];
const props = Object.keys(obj); const props = Object.keys(obj);
const propObj = props.filter((e: string): boolean => { const propObj = props.filter((e: string): boolean => {
@ -422,17 +434,17 @@ class Dumper {
const prop = k[i]; const prop = k[i];
const value = obj[prop]; const value = obj[prop];
if (value instanceof Date) { if (value instanceof Date) {
out.push(this._dateDeclaration(prop, value)); out.push(this._dateDeclaration([prop], value));
} else if (typeof value === "string" || value instanceof RegExp) { } else if (typeof value === "string" || value instanceof RegExp) {
out.push(this._strDeclaration(prop, value.toString())); out.push(this._strDeclaration([prop], value.toString()));
} else if (typeof value === "number") { } else if (typeof value === "number") {
out.push(this._numberDeclaration(prop, value)); out.push(this._numberDeclaration([prop], value));
} else if ( } else if (
value instanceof Array && value instanceof Array &&
this._isSimplySerializable(value[0]) this._isSimplySerializable(value[0])
) { ) {
// only if primitives types in the array // only if primitives types in the array
out.push(this._arrayDeclaration(prop, value)); out.push(this._arrayDeclaration([prop], value));
} else if ( } else if (
value instanceof Array && value instanceof Array &&
!this._isSimplySerializable(value[0]) !this._isSimplySerializable(value[0])
@ -440,15 +452,15 @@ class Dumper {
// array of objects // array of objects
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {
out.push(""); out.push("");
out.push(this._headerGroup(path + prop)); out.push(this._headerGroup([...keys, prop]));
out.push(...this._parse(value[i], `${path}${prop}.`)); out.push(...this._parse(value[i], [...keys, prop]));
} }
} else if (typeof value === "object") { } else if (typeof value === "object") {
out.push(""); out.push("");
out.push(this._header(path + prop)); out.push(this._header([...keys, prop]));
if (value) { if (value) {
const toParse = value as Record<string, unknown>; const toParse = value as Record<string, unknown>;
out.push(...this._parse(toParse, `${path}${prop}.`)); out.push(...this._parse(toParse, [...keys, prop]));
} }
// out.push(...this._parse(value, `${path}${prop}.`)); // out.push(...this._parse(value, `${path}${prop}.`));
} }
@ -465,35 +477,36 @@ class Dumper {
value instanceof Array value instanceof Array
); );
} }
_header(title: string): string { _header(keys: string[]): string {
return `[${title}]`; return `[${joinKeys(keys)}]`;
} }
_headerGroup(title: string): string { _headerGroup(keys: string[]): string {
return `[[${title}]]`; return `[[${joinKeys(keys)}]]`;
} }
_declaration(title: string): string { _declaration(keys: string[]): string {
const title = joinKeys(keys);
if (title.length > this.maxPad) { if (title.length > this.maxPad) {
this.maxPad = title.length; this.maxPad = title.length;
} }
return `${title} = `; return `${title} = `;
} }
_arrayDeclaration(title: string, value: unknown[]): string { _arrayDeclaration(keys: string[], value: unknown[]): string {
return `${this._declaration(title)}${JSON.stringify(value)}`; return `${this._declaration(keys)}${JSON.stringify(value)}`;
} }
_strDeclaration(title: string, value: string): string { _strDeclaration(keys: string[], value: string): string {
return `${this._declaration(title)}"${value}"`; return `${this._declaration(keys)}"${value}"`;
} }
_numberDeclaration(title: string, value: number): string { _numberDeclaration(keys: string[], value: number): string {
switch (value) { switch (value) {
case Infinity: case Infinity:
return `${this._declaration(title)}inf`; return `${this._declaration(keys)}inf`;
case -Infinity: case -Infinity:
return `${this._declaration(title)}-inf`; return `${this._declaration(keys)}-inf`;
default: default:
return `${this._declaration(title)}${value}`; return `${this._declaration(keys)}${value}`;
} }
} }
_dateDeclaration(title: string, value: Date): string { _dateDeclaration(keys: string[], value: Date): string {
function dtPad(v: string, lPad: number = 2): string { function dtPad(v: string, lPad: number = 2): string {
return pad(v, lPad, { char: "0" }); return pad(v, lPad, { char: "0" });
} }
@ -505,7 +518,7 @@ class Dumper {
const ms = dtPad(value.getUTCMilliseconds().toString(), 3); const ms = dtPad(value.getUTCMilliseconds().toString(), 3);
// formated date // formated date
const fData = `${value.getUTCFullYear()}-${m}-${d}T${h}:${min}:${s}.${ms}`; const fData = `${value.getUTCFullYear()}-${m}-${d}T${h}:${min}:${s}.${ms}`;
return `${this._declaration(title)}${fData}`; return `${this._declaration(keys)}${fData}`;
} }
_format(): string[] { _format(): string[] {
const rDeclaration = /(.*)\s=/; const rDeclaration = /(.*)\s=/;
@ -542,9 +555,7 @@ class Dumper {
} }
export function stringify(srcObj: object): string { export function stringify(srcObj: object): string {
let out: string[] = []; return new Dumper(srcObj).dump().join("\n");
out = new Dumper(srcObj).dump();
return out.join("\n");
} }
export function parse(tomlString: string): object { export function parse(tomlString: string): object {

View file

@ -1,5 +1,5 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { test } from "../testing/mod.ts"; import { runIfMain, test } from "../testing/mod.ts";
import { assertEquals } from "../testing/asserts.ts"; import { assertEquals } from "../testing/asserts.ts";
import { existsSync } from "../fs/exists.ts"; import { existsSync } from "../fs/exists.ts";
import { readFileStrSync } from "../fs/read_file_str.ts"; import { readFileStrSync } from "../fs/read_file_str.ts";
@ -301,6 +301,17 @@ test({
const src = { const src = {
foo: { bar: "deno" }, foo: { bar: "deno" },
this: { is: { nested: "denonono" } }, this: { is: { nested: "denonono" } },
"https://deno.land/std": {
$: "doller"
},
"##": {
deno: {
"https://deno.land": {
proto: "https",
":80": "port"
}
}
},
arrayObjects: [{ stuff: "in" }, {}, { the: "array" }], arrayObjects: [{ stuff: "in" }, {}, { the: "array" }],
deno: "is", deno: "is",
not: "[node]", not: "[node]",
@ -376,6 +387,13 @@ bar = "deno"
[this.is] [this.is]
nested = "denonono" nested = "denonono"
["https://deno.land/std"]
"$" = "doller"
["##".deno."https://deno.land"]
proto = "https"
":80" = "port"
[[arrayObjects]] [[arrayObjects]]
stuff = "in" stuff = "in"
@ -388,3 +406,5 @@ the = "array"
assertEquals(actual, expected); assertEquals(actual, expected);
} }
}); });
runIfMain(import.meta);