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