-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmarc-node
executable file
·200 lines (190 loc) · 5.58 KB
/
marc-node
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
196
197
198
199
200
#!/usr/bin/node
const ESrequire = require("./src/ESrequire.js");
const MarcParser = ESrequire("./src/parser.js");
const MarcHelper = ESrequire("./src/helper.js");
const CLI = require("./src/CLI.js");
const fs = require("fs");
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: false,
terminal: false
});
const ARGS_STRUCTURE = {
//Structure of expected arguments, used to generate help & check before exec
process: process.argv[1].split("/").slice(-1),
mandatory: {
"command":["display","filter","extract","help"],
"file":[]
},
optional: [
"encoding",
"record-separator",
"field-separator",
"subfield-separator",
"format",
"fields",
"values"
],
};
const checkArgs = (arguments) => {
// Checks arguments consistency w/ expected structure, raise errors otherwise
Object.keys(ARGS_STRUCTURE.mandatory).map(e => {
if (typeof arguments[e] === "undefined") {
throw new Error("Mandatory argument: "+e)
} else {
if (ARGS_STRUCTURE.mandatory[e].length > 0) {
if (!ARGS_STRUCTURE.mandatory[e].some(value => value == arguments[e])) {
throw new Error("Mandatory argument "+e+" takes one of values: "+
ARGS_STRUCTURE.mandatory[e].join(", "));
}
}
}
});
Object.keys(arguments).map(e => {
if (!((Object.keys(ARGS_STRUCTURE.mandatory).some(value => e == value)) ||
(ARGS_STRUCTURE.optional.some(value => e == value)))) {
console.log(CLI.invert("WARNING:")+"\tUnknown argument \""+e+"\"\n")
}
})
}
const ARGS = (() => {
// where the actual arguments are mapped for further use
let args = {};
process.argv.slice(2).map((e,i) => {
if (i == 0) {
return ["command",e];
} else if (i == 1) {
return ["file",e];
} else {
if (e.slice(0,2) == "--") {
return e.slice(2).split("=");
} else {
return [e,true];
}
}
}).map(e => { args[e[0]] = e[1] });
return args;
})();
const getFile = (path) => fs.readFileSync(path,(ARGS.encoding || "utf8"));
const DIE = (e) => {
//shows errors & displays help
console.log(CLI.invert(e)+"\n");
commands.help();
}
const getParams = () => {
//maps CLI arguments to parameters object structure expected by the MarcParser
let params = {};
if (ARGS["field-separator"]) {
params.fields = ARGS["field-separator"];
}
if (ARGS["subfield-separator"]) {
params.subfields = ARGS["subfield-separator"];
}
if (ARGS.fields) {
params.toParse = ARGS.fields;
}
return params;
};
const getInput = async () => {
// handles input, whether from FS or pipe
try {
if (ARGS.file != "-") {
return getFile(ARGS.file);
} else {
return await (() => {
return new Promise((resolve,reject) => {
rl.on("line", (line) => {
resolve(line);
});
});
})();
}
} catch {
DIE(new Error("couldn't open input file"));
}
};
const splitRecords = (batch) => {
//splits a batch of records into an array, using the record separator (default \u001d)
return batch
.split((ARGS["record-separator"] || MarcParser.MARC.recordSeparator))
.filter(e => ["","\n"].indexOf(e) < 0);
};
const commands = {
//map of available commands
display:async () => {
let records = await getInput();
let parameters = getParams();
ARGS.format = (ARGS.format || false);
records = splitRecords(records);
records = await Promise.all(records
.map(async record => {
record = MarcParser.parseRecord(record,parameters);
if (ARGS.format) {
record = await MarcHelper.explainRecord(record,ARGS.format);
}
CLI.displayRecord(record,ARGS.format);
console.log("");
}));
process.exit();
},
help: () => {
let help = `Usage: `+ARGS_STRUCTURE.process+` COMMAND FILE [OPTIONS]\n
If FILE is -, read stdin.\n\nCommands:\n\t`
+ARGS_STRUCTURE.mandatory.command.join("\n\t")+`\n\nOptions:\t\tSyntax: --KEY=VALUE
\t`+ARGS_STRUCTURE.optional.join("\n\t")+`\n`;
console.log(help);
process.exit();
},
filter: async () => {
let records = await getInput();
let parameters = getParams();
if (typeof parameters.toParse === "undefined") {
DIE(new Error("--fields option is required for record extraction"));
process.exit();
}
if (typeof ARGS.values === "undefined") {
DIE(new Error("--values option is required for record extraction"));
process.exit();
}
records = splitRecords(records);
records = await Promise.all(
records.map(async (e,i) => {
e = MarcParser.parseRecord(e,parameters);
e = (MarcParser.filterRecord(e,parameters.toParse,ARGS.values.split(",")))
? e.rawRecord
: false;
return e;
})
);
console.log(records
.filter(e => e !== false)
.join(((ARGS["record-separator"]
|| MarcParser.MARC.recordSeparator))));
process.exit();
},
extract: async () => {
let output = [];
let records = await getInput();
let parameters = getParams();
if (typeof parameters.toParse === "undefined") {
DIE(new Error("--fields option is required for data extraction"));
process.exit();
}
records = splitRecords(records);
records = await Promise.all(records
.map(async record => {
record = MarcParser.parseRecord(record,parameters);
output.push({leader:record.leader,fields:record.fields});
console.log("");
}));
console.log(JSON.stringify(output));
process.exit();
}
};
try {
checkArgs(ARGS);
commands[ARGS.command]();
} catch (e) {
DIE(e);
}