Skip to content

Commit 1e00039

Browse files
committed
Add KvSerializer
1 parent f72c1d5 commit 1e00039

File tree

3 files changed

+137
-33
lines changed

3 files changed

+137
-33
lines changed

packages/kv/src/main.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { Token, TokenList, TokenType, Position, Literal, Range, Item, ParseError
33
//import { formatAll, FormattingOptions } from "./formatter";
44
import { isWhitespace, isQuoted, stripQuotes, isFloatValue, isIntegerValue, isScalarValue } from "./string-util";
55
import { parseText, parseTokens } from "./parser";
6-
import { serialize } from "./serializer"
6+
import { KvSerializer } from "./serializer";
77

88
export {
99
tokenize,
1010
Token, TokenList, TokenType, Position, Literal as PositionedLiteral, Range, Item, ParseError, ParseErrorType, Document,
1111
//formatAll, FormattingOptions,
1212
isWhitespace, isQuoted, stripQuotes, isFloatValue, isIntegerValue, isScalarValue,
1313
parseText, parseTokens,
14-
serialize
14+
KvSerializer
1515
};

packages/kv/src/serializer.test.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import {serialize} from "./serializer";
1+
import {KvSerializer} from "./serializer";
2+
import {parseText} from "./parser";
23

34
test("Serialize Object simple", () => {
45
const obj = {
@@ -9,7 +10,7 @@ test("Serialize Object simple", () => {
910
dateProp: new Date(2004, 10, 11, 8, 10, 0),
1011
};
1112

12-
const kvStr = serialize(obj);
13+
const kvStr = KvSerializer.serialize(obj);
1314
expect(kvStr).toBe(`"Object" {
1415
"prop1" "string val"
1516
"prop2" "10"
@@ -28,12 +29,43 @@ test("Serialize Object nested", () => {
2829
}
2930
};
3031

31-
const kvStr = serialize(obj);
32+
const kvStr = KvSerializer.serialize(obj);
3233
expect(kvStr).toBe(`"Object" {
3334
"prop1" "string val"
3435
"sub" {
3536
"more" "10"
3637
"stuff" "but nested"
3738
}
3839
}`);
40+
});
41+
42+
test("Deserialize Item", () => {
43+
const doc = parseText(`"Object" {
44+
"prop1" "string val"
45+
"sub" {
46+
"more" "10"
47+
"stuff" "but nested"
48+
boolprop false
49+
boolpropa true
50+
}
51+
52+
"another prop" with multiple values
53+
"floatprop" 4.50
54+
"intprop" 90000
55+
}`);
56+
57+
const nodes = KvSerializer.deserialize(doc);
58+
expect(nodes).toHaveLength(1);
59+
const result = nodes[0];
60+
61+
expect(result).toBeDefined();
62+
expect(result!.prop1).toBe("string val");
63+
expect(result!.sub).toBeDefined();
64+
expect(result).toHaveProperty("sub.more", 10);
65+
expect(result).toHaveProperty("sub.stuff", "but nested");
66+
expect(result).toHaveProperty("sub.boolprop", false);
67+
expect(result).toHaveProperty("sub.boolpropa", true);
68+
expect(result).toHaveProperty("another prop", ["with", "multiple", "values"]);
69+
expect(result).toHaveProperty("floatprop", 4.5);
70+
expect(result).toHaveProperty("intprop", 90000);
3971
});

packages/kv/src/serializer.ts

Lines changed: 100 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,108 @@
1-
export function serialize(obj: Object, options?: { name?: string, indentSpaces?: number, indentTabs?: boolean }, nestedLevel: number = 0): string {
2-
let indentObjStr: string = "";
3-
let indentStrKv: string = "";
4-
let indentChar = "";
5-
if(options?.indentTabs) {
6-
indentChar = "\t";
7-
} else {
8-
for (let i = 0; i < (options?.indentSpaces ?? 2); i++) {
9-
indentChar += " ";
1+
import {Document, Item, Literal} from "./parser-types";
2+
3+
4+
type KvNodeValue = string | number | boolean;
5+
type KvNode = { [key: string]: KvNodeValue | KvNodeValue[] | KvNode };
6+
7+
8+
export const KvSerializer = {
9+
10+
serialize(obj: Record<string, unknown>, options?: { name?: string, indentSpaces?: number, indentTabs?: boolean }, nestedLevel = 0): string {
11+
let indentObjStr = "";
12+
let indentStrKv = "";
13+
let indentChar = "";
14+
if(options?.indentTabs) {
15+
indentChar = "\t";
16+
} else {
17+
for (let i = 0; i < (options?.indentSpaces ?? 2); i++) {
18+
indentChar += " ";
19+
}
1020
}
21+
for (let i = 0; i < nestedLevel; i++) {
22+
indentObjStr += indentChar;
23+
}
24+
indentStrKv = indentObjStr + indentChar;
25+
26+
const kvObjHead = `${indentObjStr}"${options?.name ?? "Object"}" {`;
27+
const kvLines: string[] = [kvObjHead];
28+
for (const [key, val] of Object.entries(obj)) {
29+
const valIsObj = typeof val === "object";
30+
const objHasCustomToString = typeof val?.toString === "function" && val.toString !== Object.prototype.toString;
31+
const doSerializeObj = valIsObj && !objHasCustomToString;
32+
33+
if(doSerializeObj) {
34+
const serialized = KvSerializer.serialize(val as Record<string, unknown>, { ...options, name: key }, nestedLevel + 1);
35+
kvLines.push(serialized);
36+
} else {
37+
const kvString = `${indentStrKv}"${key}" "${String(val)}"`;
38+
kvLines.push(kvString);
39+
}
40+
}
41+
kvLines.push(`${indentObjStr}}`);
42+
43+
const fullStr = kvLines.join("\n");
44+
return fullStr;
45+
},
46+
47+
deserialize(doc: Document): KvNode[] {
48+
return doc.getRootItems().map(_deserializeItem).filter(i => i !== undefined) as KvNode[];
1149
}
12-
for (let i = 0; i < nestedLevel; i++) {
13-
indentObjStr += indentChar;
50+
};
51+
52+
function _deserializeItem(item: Item): KvNode | undefined {
53+
const children = item.getChildren();
54+
if (!children) {
55+
return;
1456
}
15-
indentStrKv = indentObjStr + indentChar;
1657

17-
const kvObjHead = `${indentObjStr}"${options?.name ?? "Object"}" {`;
18-
const kvLines: string[] = [kvObjHead];
19-
for (const [key, val] of Object.entries(obj)) {
20-
const valIsObj = typeof val === "object";
21-
const objHasCustomToString = typeof val.toString === "function" && val.toString !== Object.prototype.toString;
22-
const doSerializeObj = valIsObj && !objHasCustomToString;
58+
const node: KvNode = {};
59+
for (const child of children) {
60+
const values = child.getValues();
61+
const hasChildren = child.getChildren() !== null;
62+
const name = child.getKey().getUnquotedContent();
2363

24-
if(doSerializeObj) {
25-
const serialized = serialize(val, { ...options, name: key }, nestedLevel + 1);
26-
kvLines.push(serialized);
27-
} else {
28-
const kvString = `${indentStrKv}"${key}" "${String(val)}"`;
29-
kvLines.push(kvString);
64+
if(values) {
65+
const val = _deserializeItemValue(values);
66+
if (val !== undefined) {
67+
node[name] = val;
68+
}
69+
} else if(hasChildren) {
70+
const childName = child.getKey().getUnquotedContent();
71+
const childItem = _deserializeItem(child);
72+
if(childItem) {
73+
node[childName] = childItem;
74+
}
3075
}
3176
}
32-
kvLines.push(`${indentObjStr}}`);
3377

34-
const fullStr = kvLines.join("\n");
35-
return fullStr;
36-
}
78+
return node;
79+
}
80+
81+
function _deserializeItemValue(vals: Literal[]): KvNodeValue | KvNodeValue[] | undefined {
82+
if(vals.length === 1) {
83+
return _transformKvValue(vals[0].getUnquotedContent());
84+
} else {
85+
return vals.map(v => _transformKvValue(v.getUnquotedContent()));
86+
}
87+
}
88+
89+
function _transformKvValue(val: string): KvNodeValue {
90+
const float = Number.parseFloat(val);
91+
if(!Number.isNaN(float)) {
92+
return float;
93+
}
94+
95+
const int = Number.parseInt(val);
96+
if(!Number.isNaN(int)) {
97+
return int;
98+
}
99+
100+
if(val === "true") {
101+
return true;
102+
}
103+
if(val === "false") {
104+
return false;
105+
}
106+
107+
return val;
108+
}

0 commit comments

Comments
 (0)