forked from vimsucks/ezini
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ezini.js
127 lines (114 loc) · 3.04 KB
/
ezini.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
const os = require("os")
/**
* Parse a INI-format string to an object
* @param {string} str INI-format string
* @returns {Object} Object parsed from the given string
*/
function parseSync(str) {
const output = {}
let section = null
if (str.trim().length === 0) {
return {}
}
const lines = str.split(os.EOL)
lines.forEach((rawLine) => {
// skip if empty or comment line
// remove comment
const line = rawLine.replace(/;.*/, "")
if (line.trim().length === 0) return
// if this line is section
let match = line.match(/^\[(.*)]$/)
if (match && match[1] !== undefined) {
section = match[1].trim()
output[section] = {}
} else {
match = line.match(/^(.*)=(.*)$/)
if (match && match[1] !== undefined && match[2] !== undefined) {
// if value is a boolean value
const key = match[1].trim()
let value = match[2].trim()
if (value.toLowerCase() === "true" || value.toLowerCase() === "false") {
value = !!value
} else if (!isNaN(value)) {
// if value is a number
value = +value
} else {
// regard value as string
value = value.replace(/^"|"$/g, "")
}
if (section === null) {
output[match[1].trim()] = match[2].trim().replace(/^"|"$/g, "")
output[key] = value
} else {
output[section][key] = value
}
}
}
})
return output
}
/**
* Async wrapper of function parseSync
* @param {string} str INI-format string
* @param callback Callback after parsing complete,
* should have one parameter: obj(the parsed object)
*/
function parse(str, callback) {
process.nextTick(() => {
const output = parseSync(str)
callback(output)
})
}
/**
* Stringify an object to an ini-format string
* @param {Object} obj Object to be stringify
* @returns {string} INI-format string which is stringified from given object
*/
function stringifySync(obj) {
let output = ""
let firstOccur = true
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === "string") {
output += `${key}=${obj[key]}`
output += os.EOL
} else {
if (firstOccur) {
firstOccur = false
} else {
output += os.EOL
}
output += `[${key}]`
output += os.EOL
Object.keys(obj[key]).forEach((innerKey) => {
let value = obj[key][innerKey]
if (typeof value === "string") {
// if value can ber converted to number or boolean,
// but it should be a string,
// so keep the quotes to indicate its type
if (!isNaN(value) || value.toLowerCase() === "true" || value.toLowerCase() === "false") {
value = `"${value}"`
}
}
output += `${innerKey}=${value}`
output += os.EOL
})
}
})
return output
}
/**
* Async wrapper of function stringifySync
* @param {Object} obj Object to be stringify
* @param callback Callback after parsing complete,
* should have one parameter: str(stringified from given object)
*/
function stringify(obj, callback) {
process.nextTick(() => {
const str = stringifySync(obj)
callback(str)
})
}
exports.parse = parse
exports.parseSync = parseSync
exports.stringify = stringify
exports.stringifySync = stringifySync