mirror of
https://github.com/denoland/deno.git
synced 2025-03-10 22:29:09 -04:00
toml: add Stringify feature (denoland/deno_std#319)
Original: 1e589b9553
This commit is contained in:
parent
f8f5611350
commit
ae9148752c
4 changed files with 265 additions and 4 deletions
|
@ -3,9 +3,9 @@
|
||||||
/** FillOption Object */
|
/** FillOption Object */
|
||||||
export interface FillOption {
|
export interface FillOption {
|
||||||
/** Char to fill in */
|
/** Char to fill in */
|
||||||
char: string;
|
char?: string;
|
||||||
/** Side to fill in */
|
/** Side to fill in */
|
||||||
side: "left" | "right";
|
side?: "left" | "right";
|
||||||
/** If strict, output string can't be greater than strLen*/
|
/** If strict, output string can't be greater than strLen*/
|
||||||
strict?: boolean;
|
strict?: boolean;
|
||||||
/** char/string used to specify the string has been truncated */
|
/** char/string used to specify the string has been truncated */
|
||||||
|
@ -54,7 +54,7 @@ export function pad(
|
||||||
let out = input;
|
let out = input;
|
||||||
const outL = out.length;
|
const outL = out.length;
|
||||||
if (outL < strLen) {
|
if (outL < strLen) {
|
||||||
if (opts.side === "left") {
|
if (!opts.side || opts.side === "left") {
|
||||||
out = out.padStart(strLen, opts.char);
|
out = out.padStart(strLen, opts.char);
|
||||||
} else {
|
} else {
|
||||||
out = out.padEnd(strLen, opts.char);
|
out = out.padEnd(strLen, opts.char);
|
||||||
|
|
|
@ -91,6 +91,8 @@ will output:
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### Parse
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { parseFile, parse } from "./parser.ts";
|
import { parseFile, parse } from "./parser.ts";
|
||||||
|
|
||||||
|
@ -99,3 +101,17 @@ const tomlObject = parseFile("file.toml");
|
||||||
const tomlString = 'foo.bar = "Deno"';
|
const tomlString = 'foo.bar = "Deno"';
|
||||||
const tomlObject22 = parse(tomlString);
|
const tomlObject22 = parse(tomlString);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Stringify
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { stringify } from "./parser.ts";
|
||||||
|
const obj = {
|
||||||
|
bin: [
|
||||||
|
{ name: "deno", path: "cli/main.rs" },
|
||||||
|
{ name: "deno_core", path: "src/foo.rs" }
|
||||||
|
],
|
||||||
|
nib: [{ name: "node", path: "not_found" }]
|
||||||
|
};
|
||||||
|
const tomlString = stringify(obj);
|
||||||
|
```
|
||||||
|
|
151
toml/parser.ts
151
toml/parser.ts
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
import { existsSync } from "../fs/exists.ts";
|
import { existsSync } from "../fs/exists.ts";
|
||||||
import { deepAssign } from "../util/deep_assign.ts";
|
import { deepAssign } from "../util/deep_assign.ts";
|
||||||
|
import { pad } from "../strings/pad.ts";
|
||||||
|
|
||||||
class KeyValuePair {
|
class KeyValuePair {
|
||||||
key: string;
|
key: string;
|
||||||
|
@ -381,6 +382,156 @@ class Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Dumper {
|
||||||
|
maxPad: number = 0;
|
||||||
|
srcObject: object;
|
||||||
|
output: string[] = [];
|
||||||
|
constructor(srcObjc: object) {
|
||||||
|
this.srcObject = srcObjc;
|
||||||
|
}
|
||||||
|
dump(): string[] {
|
||||||
|
this.output = this._parse(this.srcObject);
|
||||||
|
this.output = this._format();
|
||||||
|
return this.output;
|
||||||
|
}
|
||||||
|
_parse(obj: object, path: string = ""): string[] {
|
||||||
|
const out = [];
|
||||||
|
const props = Object.keys(obj);
|
||||||
|
const propObj = props.filter(
|
||||||
|
e =>
|
||||||
|
(obj[e] instanceof Array && !this._isSimplySerializable(obj[e][0])) ||
|
||||||
|
!this._isSimplySerializable(obj[e])
|
||||||
|
);
|
||||||
|
const propPrim = props.filter(
|
||||||
|
e =>
|
||||||
|
!(obj[e] instanceof Array && !this._isSimplySerializable(obj[e][0])) &&
|
||||||
|
this._isSimplySerializable(obj[e])
|
||||||
|
);
|
||||||
|
const k = propPrim.concat(propObj);
|
||||||
|
for (let i = 0; i < k.length; i++) {
|
||||||
|
const prop = k[i];
|
||||||
|
const value = obj[prop];
|
||||||
|
if (value instanceof Date) {
|
||||||
|
out.push(this._dateDeclaration(prop, value));
|
||||||
|
} else if (typeof value === "string" || value instanceof RegExp) {
|
||||||
|
out.push(this._strDeclaration(prop, value.toString()));
|
||||||
|
} else if (typeof value === "number") {
|
||||||
|
out.push(this._numberDeclaration(prop, value));
|
||||||
|
} else if (
|
||||||
|
value instanceof Array &&
|
||||||
|
this._isSimplySerializable(value[0])
|
||||||
|
) {
|
||||||
|
// only if primitives types in the array
|
||||||
|
out.push(this._arrayDeclaration(prop, value));
|
||||||
|
} else if (
|
||||||
|
value instanceof Array &&
|
||||||
|
!this._isSimplySerializable(value[0])
|
||||||
|
) {
|
||||||
|
// array of objects
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
out.push("");
|
||||||
|
out.push(this._headerGroup(path + prop));
|
||||||
|
out.push(...this._parse(value[i], `${path}${prop}.`));
|
||||||
|
}
|
||||||
|
} else if (typeof value === "object") {
|
||||||
|
out.push("");
|
||||||
|
out.push(this._header(path + prop));
|
||||||
|
out.push(...this._parse(value, `${path}${prop}.`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.push("");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
_isSimplySerializable(value: unknown): boolean {
|
||||||
|
return (
|
||||||
|
typeof value === "string" ||
|
||||||
|
typeof value === "number" ||
|
||||||
|
value instanceof RegExp ||
|
||||||
|
value instanceof Date ||
|
||||||
|
value instanceof Array
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_header(title: string): string {
|
||||||
|
return `[${title}]`;
|
||||||
|
}
|
||||||
|
_headerGroup(title: string): string {
|
||||||
|
return `[[${title}]]`;
|
||||||
|
}
|
||||||
|
_declaration(title: string): string {
|
||||||
|
if (title.length > this.maxPad) {
|
||||||
|
this.maxPad = title.length;
|
||||||
|
}
|
||||||
|
return `${title} = `;
|
||||||
|
}
|
||||||
|
_arrayDeclaration(title: string, value: unknown[]): string {
|
||||||
|
return `${this._declaration(title)}${JSON.stringify(value)}`;
|
||||||
|
}
|
||||||
|
_strDeclaration(title: string, value: string): string {
|
||||||
|
return `${this._declaration(title)}"${value}"`;
|
||||||
|
}
|
||||||
|
_numberDeclaration(title: string, value: number): string {
|
||||||
|
switch (value) {
|
||||||
|
case Infinity:
|
||||||
|
return `${this._declaration(title)}inf`;
|
||||||
|
case -Infinity:
|
||||||
|
return `${this._declaration(title)}-inf`;
|
||||||
|
default:
|
||||||
|
return `${this._declaration(title)}${value}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_dateDeclaration(title: string, value: Date): string {
|
||||||
|
function dtPad(v: string, lPad: number = 2): string {
|
||||||
|
return pad(v, lPad, { char: "0" });
|
||||||
|
}
|
||||||
|
let m = dtPad((value.getUTCMonth() + 1).toString());
|
||||||
|
let d = dtPad(value.getUTCDate().toString());
|
||||||
|
const h = dtPad(value.getUTCHours().toString());
|
||||||
|
const min = dtPad(value.getUTCMinutes().toString());
|
||||||
|
const s = dtPad(value.getUTCSeconds().toString());
|
||||||
|
const ms = dtPad(value.getUTCMilliseconds().toString(), 3);
|
||||||
|
const fmtDate = `${value.getUTCFullYear()}-${m}-${d}T${h}:${min}:${s}.${ms}`;
|
||||||
|
return `${this._declaration(title)}${fmtDate}`;
|
||||||
|
}
|
||||||
|
_format(): string[] {
|
||||||
|
const rDeclaration = /(.*)\s=/;
|
||||||
|
const out = [];
|
||||||
|
for (let i = 0; i < this.output.length; i++) {
|
||||||
|
const l = this.output[i];
|
||||||
|
// we keep empty entry for array of objects
|
||||||
|
if (l[0] === "[" && l[1] !== "[") {
|
||||||
|
// empty object
|
||||||
|
if (this.output[i + 1] === "") {
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.push(l);
|
||||||
|
} else {
|
||||||
|
const m = rDeclaration.exec(l);
|
||||||
|
if (m) {
|
||||||
|
out.push(l.replace(m[1], pad(m[1], this.maxPad, { side: "right" })));
|
||||||
|
} else {
|
||||||
|
out.push(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Cleaning multiple spaces
|
||||||
|
const cleanedOutput = [];
|
||||||
|
for (let i = 0; i < out.length; i++) {
|
||||||
|
const l = out[i];
|
||||||
|
if (!(l === "" && out[i + 1] === "")) {
|
||||||
|
cleanedOutput.push(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cleanedOutput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stringify(srcObj: object): string {
|
||||||
|
let out: string[] = [];
|
||||||
|
out = new Dumper(srcObj).dump();
|
||||||
|
return out.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
export function parse(tomlString: string): object {
|
export function parse(tomlString: string): object {
|
||||||
// File is potentially using EOL CRLF
|
// File is potentially using EOL CRLF
|
||||||
tomlString = tomlString.replace(/\r\n/g, "\n").replace(/\\\n/g, "\n");
|
tomlString = tomlString.replace(/\r\n/g, "\n").replace(/\\\n/g, "\n");
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// 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 { test } from "../testing/mod.ts";
|
||||||
import { assertEquals } from "../testing/asserts.ts";
|
import { assertEquals } from "../testing/asserts.ts";
|
||||||
import { parseFile } from "./parser.ts";
|
import { parseFile, stringify } from "./parser.ts";
|
||||||
import * as path from "../fs/path/mod.ts";
|
import * as path from "../fs/path/mod.ts";
|
||||||
const testFilesDir = path.resolve("toml", "testdata");
|
const testFilesDir = path.resolve("toml", "testdata");
|
||||||
|
|
||||||
|
@ -282,3 +282,97 @@ test({
|
||||||
assertEquals(actual, expected);
|
assertEquals(actual, expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Stringify",
|
||||||
|
fn() {
|
||||||
|
const src = {
|
||||||
|
foo: { bar: "deno" },
|
||||||
|
this: { is: { nested: "denonono" } },
|
||||||
|
arrayObjects: [{ stuff: "in" }, {}, { the: "array" }],
|
||||||
|
deno: "is",
|
||||||
|
not: "[node]",
|
||||||
|
regex: "<ic*s*>",
|
||||||
|
NANI: "何?!",
|
||||||
|
comment: "Comment inside # the comment",
|
||||||
|
int1: 99,
|
||||||
|
int2: 42,
|
||||||
|
int3: 0,
|
||||||
|
int4: -17,
|
||||||
|
int5: 1000,
|
||||||
|
int6: 5349221,
|
||||||
|
int7: 12345,
|
||||||
|
flt1: 1.0,
|
||||||
|
flt2: 3.1415,
|
||||||
|
flt3: -0.01,
|
||||||
|
flt4: 5e22,
|
||||||
|
flt5: 1e6,
|
||||||
|
flt6: -2e-2,
|
||||||
|
flt7: 6.626e-34,
|
||||||
|
odt1: new Date("1979-05-01T07:32:00Z"),
|
||||||
|
odt2: new Date("1979-05-27T00:32:00-07:00"),
|
||||||
|
odt3: new Date("1979-05-27T00:32:00.999999-07:00"),
|
||||||
|
odt4: new Date("1979-05-27 07:32:00Z"),
|
||||||
|
ld1: new Date("1979-05-27"),
|
||||||
|
reg: /foo[bar]/,
|
||||||
|
sf1: Infinity,
|
||||||
|
sf2: Infinity,
|
||||||
|
sf3: -Infinity,
|
||||||
|
sf4: NaN,
|
||||||
|
sf5: NaN,
|
||||||
|
sf6: NaN,
|
||||||
|
data: [["gamma", "delta"], [1, 2]],
|
||||||
|
hosts: ["alpha", "omega"]
|
||||||
|
};
|
||||||
|
const expected = `deno = "is"
|
||||||
|
not = "[node]"
|
||||||
|
regex = "<ic*s*>"
|
||||||
|
NANI = "何?!"
|
||||||
|
comment = "Comment inside # the comment"
|
||||||
|
int1 = 99
|
||||||
|
int2 = 42
|
||||||
|
int3 = 0
|
||||||
|
int4 = -17
|
||||||
|
int5 = 1000
|
||||||
|
int6 = 5349221
|
||||||
|
int7 = 12345
|
||||||
|
flt1 = 1
|
||||||
|
flt2 = 3.1415
|
||||||
|
flt3 = -0.01
|
||||||
|
flt4 = 5e+22
|
||||||
|
flt5 = 1000000
|
||||||
|
flt6 = -0.02
|
||||||
|
flt7 = 6.626e-34
|
||||||
|
odt1 = 1979-05-01T07:32:00.000
|
||||||
|
odt2 = 1979-05-27T07:32:00.000
|
||||||
|
odt3 = 1979-05-27T07:32:00.999
|
||||||
|
odt4 = 1979-05-27T07:32:00.000
|
||||||
|
ld1 = 1979-05-27T00:00:00.000
|
||||||
|
reg = "/foo[bar]/"
|
||||||
|
sf1 = inf
|
||||||
|
sf2 = inf
|
||||||
|
sf3 = -inf
|
||||||
|
sf4 = NaN
|
||||||
|
sf5 = NaN
|
||||||
|
sf6 = NaN
|
||||||
|
data = [["gamma","delta"],[1,2]]
|
||||||
|
hosts = ["alpha","omega"]
|
||||||
|
|
||||||
|
[foo]
|
||||||
|
bar = "deno"
|
||||||
|
|
||||||
|
[this.is]
|
||||||
|
nested = "denonono"
|
||||||
|
|
||||||
|
[[arrayObjects]]
|
||||||
|
stuff = "in"
|
||||||
|
|
||||||
|
[[arrayObjects]]
|
||||||
|
|
||||||
|
[[arrayObjects]]
|
||||||
|
the = "array"
|
||||||
|
`;
|
||||||
|
const actual = stringify(src);
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue