forked from WeNeedHome/SummaryOfLoanSuspension
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenPropertiesFromReadme.ts
195 lines (163 loc) · 5.98 KB
/
genPropertiesFromReadme.ts
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import fs from "fs";
import path from "path";
import {Errors} from "./ds/errors";
import {IFlatItem, IItem, INode} from "./ds/property";
import {
DATA_GENERATED_DIR,
ITEM_SEP,
README_PATH,
REG_CITY,
REG_END,
REG_ITEM,
REG_PROV,
REG_START,
REG_TOTAL
} from "./const";
const collectProv = () => curData.children.push(curProv)
const collectCity = () => curProv.children.push(curCity)
const collectItem = () => {
curCity.children.push(curItem)
flatItems.push({...curItem, province: curProv.name, city: curCity.name})
}
/**
* 提取:省份名,省内数量
* @param line
*/
const parseProvLine = (line: string): string => {
// 非首次匹配,则回收上一个省份
if (curProv.name) collectProv()
let matched = line.match(REG_PROV) as RegExpMatchArray
curProv = {name: matched[1], count: parseInt(matched[2]), children: []}
// console.log('parsing province: ' + curProv.name) # since we have powerful error track, the log is unnecessary now
return line
}
/**
* 提取:城市名,市内数量,项目们
* @param line
*/
const parseCityLine = (line: string): string => {
/**
* 拆分 -> 【处理 + 变换(格式化文档时需要)】 -> 合并
* @param itemsStr
*/
const parseItems = (itemsStr: string): string => {
return itemsStr
.split(ITEM_SEP)
.map(itemStr => {
let matched = itemStr.match(REG_ITEM) as RegExpMatchArray
curItem = matched[3]
? {name: matched[3], uri: ''}
: {name: matched[1], uri: matched[2]}
collectItem()
return itemStr
})
.join(ITEM_SEP)
}
let matched = line.match(REG_CITY) as RegExpMatchArray
curCity = {name: matched[1], count: parseInt(matched[2]), children: []}
const itemsStrRaw = matched[3]
const itemsStrNew = parseItems(itemsStrRaw)
collectCity()
return line.replace(itemsStrRaw, itemsStrNew)
}
/**
* 逐行解析README文档
* @param line
*/
const parseLine = (line: string): string => {
// 已结束判退
if (isEnded)
return line
// 确认结束判退,并回收最后一个省份
if (REG_END.test(line)) {
isEnded = true
collectProv()
return line
}
// 总数在省份数据之前,因此先匹配
if (REG_TOTAL.test(line))
if (curData.count) throw new Error(Errors.IMPOSSIBLE)
else curData.count = parseInt((line.match(REG_TOTAL) as RegExpMatchArray)[1])
// 确认是否开始
if (REG_START.test(line))
if (isStarted) throw new Error(Errors.IMPOSSIBLE)
else isStarted = true
// 未开始判退
if (!isStarted)
return line
// 解析省份
if (REG_PROV.test(line))
return parseProvLine(line)
// 解析城市
else if (REG_CITY.test(line))
return parseCityLine(line)
return line
}
const validate = () => {
let totalCalced = 0,
totalMarked = curData.count,
errors: any[] = []
// validate province
curData.children.forEach((prov: INode) => {
let provName = prov.name,
provTotalMarked = prov.count,
provTotalCalced = 0
// validate city
prov.children.forEach((city: INode) => {
let cityName = city.name,
cityTotalCalced = city.children.length,
cityTotalMarked = city.count
provTotalCalced += cityTotalCalced
if (cityTotalCalced !== cityTotalMarked)
errors.push({cityName, cityTotalMarked, cityTotalCalced})
})
totalCalced += provTotalCalced
if (provTotalCalced !== provTotalMarked)
errors.push({provName, provTotalMarked, provTotalCalced})
})
// validate country
if (totalMarked !== totalCalced)
errors = [{totalMarked, totalCalced}, ...errors]
// 倒序打印所有报错,并中止程序
if (errors.length > 0) {
for (let i = errors.length - 1; i >= 0; i--)
console.error(errors[i])
// console.log(curData)
throw new Error(`failed to validate with ${errors.length} errors!`)
}
}
/**
* 存储结构化停贷数据
*/
const writeSuspensionData = () => {
const PROPERTIES_TREE_PATH = path.join(DATA_GENERATED_DIR, "properties-tree.json")
fs.writeFileSync(PROPERTIES_TREE_PATH, JSON.stringify(curData, null, 2), 'utf-8')
console.log('wrote properties data into file://' + PROPERTIES_TREE_PATH)
const PROPERTIES_FLAT_PATH = path.join(DATA_GENERATED_DIR, "properties-flat.json")
fs.writeFileSync(PROPERTIES_FLAT_PATH, JSON.stringify(flatItems, null, 2), 'utf-8')
console.log('wrote properties data into file://' + PROPERTIES_FLAT_PATH)
}
/**
* 重新序列化README文档
*/
const rewriteReadmeFile = () => {
const BACKUP_README_PATH = README_PATH + '.bak'
fs.cpSync(README_PATH, BACKUP_README_PATH)
console.log("backed up README (since rewriting) into file://" + BACKUP_README_PATH)
fs.writeFileSync(README_PATH, contentNew, 'utf-8')
console.log('rewrote README (since changed) into file://' + README_PATH)
}
let isStarted = false
let isEnded = false
const curData: INode = {name: '中华人民共和国', count: 0, children: []}
let curProv: INode = {name: '', count: 0, children: []}
let curCity: INode = {name: '', count: 0, children: []}
let curItem: IItem = {name: '', uri: ''}
const flatItems: IFlatItem[] = []
const contentRaw = fs.readFileSync(README_PATH, 'utf-8')
const contentNew = contentRaw.split('\n').map(parseLine).join('\n')
validate() // 验证文档是否正确解析,如无,则报错,阻止后续的写入操作
writeSuspensionData() // 输出树状的停贷数据
if (contentNew !== contentRaw) // no change, no rewrite
rewriteReadmeFile() // 如果重组后的内容有变动,则重新生成一份 readme 文档
console.log('finished √')