Skip to content

Commit 3ba8b3c

Browse files
authored
Merge pull request #439 from ger-benjamin/427_support_parsers_constructor_options
427 support parser's constructor options
2 parents 34500ab + 5a7ff8b commit 3ba8b3c

File tree

5 files changed

+130
-42
lines changed

5 files changed

+130
-42
lines changed

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ geostyler-cli --output new-qgis-style.qml my-existing.sld
2727
## Usage without installation ⚡
2828

2929
`Node.js` includes [npx](https://docs.npmjs.com/cli/v10/commands/npx), this
30-
allows you to run commands from an npm package without having to install it.
30+
allows you to run commands from a npm package without having to install it.
3131

3232
```
3333
npx geostyler-cli -s sld -t qgis -o output.qml input.sld
@@ -106,16 +106,19 @@ geostyler-cli -t sld testdata/point_simple.qml
106106
* `-h` / `--help` Display the help and exit.
107107
* `-o` / `--output` Output filename or directory. Required when the source is a directory.
108108
For a file leave this empty to write to `stdout`. [string]
109-
* `-s` / `--source` Source parser, either `mapbox`, `mapfile` or `map`,
110-
"sld" or "se" for SLD - the parser will read the version from the file,
111-
"qgis" or "qml" for QGIS QML files, and "ol-flat" for OpenLayers FlatStyles.
109+
* `-s` / `--source` Source parser, either `mapbox`, `mapfile`, `map`
110+
`sld` - the parser will read the version from the file -
111+
`qgis` or `qml` for QGIS QML files, and `ol-flat` for OpenLayers FlatStyles.
112112
If not given, it will be guessed from the extension of the input file.
113-
Mandatory if the the target is a directory.
114-
* `-t` / `--target` Target parser, either `mapbox`, `sld` (for SLD 1.0), `se` (for SLD 1.1),
115-
"qgis" or "qml" for QGIS QML files, or "ol-flat" for OpenLayers FlatStyles.
113+
Mandatory if the target is a directory.
114+
* `-t` / `--target` Target parser, either `mapbox`, `sld`,
115+
`qgis` or `qml` for QGIS QML files, or `ol-flat` for OpenLayers FlatStyles.
116116
If not given, it will be guessed from the extension of the output file.
117117
Mapfiles are not currently supported as target.
118-
Mandatory if the the target is a directory.
118+
Mandatory if the target is a directory.
119+
* `--sourceOptions` Options for the constructor of the source parser. This must
120+
be a comma-separated string like `--sourceOptions 'version:1.1.0,debug:true'`.
121+
* `--targetOptions` Same as sourceOptions, but for the target parser.
119122
* `-v` / `--version` Display the version of the program.
120123
* `--quiet` Suppress all interactive output.
121124

src/index.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
logVersion
2424
} from './logHelper.js';
2525
import path from 'path';
26+
import {getParserOptions} from './utils.js';
2627

2728
const ensureTrailingSlash = (inputString: string): string => {
2829
if (!inputString) {
@@ -31,25 +32,23 @@ const ensureTrailingSlash = (inputString: string): string => {
3132
return inputString.at(- 1) === path.sep ? inputString : `${inputString}` + path.sep;
3233
};
3334

34-
const getParserFromFormat = (inputString: string): StyleParser | undefined => {
35+
const getParserFromFormat = (inputString: string, parserOptions: Record<string, unknown>): StyleParser | undefined => {
3536
if (!inputString) {
3637
throw new Error('No input');
3738
}
3839
switch (inputString.toLowerCase()) {
3940
case 'lyrx':
4041
return new LyrxParser();
4142
case 'mapbox':
42-
return new MapboxParser();
43+
return new MapboxParser(parserOptions);
4344
case 'mapfile':
4445
case 'map':
45-
return new MapfileParser();
46+
return new MapfileParser(parserOptions);
4647
case 'sld':
47-
return new SLDParser();
48-
case 'se':
49-
return new SLDParser({ sldVersion: '1.1.0' });
48+
return new SLDParser(parserOptions);
5049
case 'qgis':
5150
case 'qml':
52-
return new QGISParser();
51+
return new QGISParser(parserOptions);
5352
case 'ol-flat':
5453
return new OlFlatStyleParser();
5554
case 'geostyler':
@@ -119,7 +118,7 @@ const computeTargetPath = (
119118
outputPath = path.normalize(outputPath);
120119

121120
// Case file -> directory
122-
// Get output name from source and add extension.
121+
// Gets output name from source and add extension.
123122
const pathElements = sourcePathFile.split(path.sep);
124123
const lastElement = pathElements?.pop();
125124
pathElements.shift();
@@ -236,8 +235,10 @@ async function main() {
236235
const {
237236
s,
238237
source,
238+
sourceOptions,
239239
t,
240240
target,
241+
targetOptions,
241242
o,
242243
output,
243244
h,
@@ -270,20 +271,20 @@ async function main() {
270271
isSilent: !!quiet || false,
271272
}).start();
272273

273-
// Check source path arg.
274+
// Check the source path arg.
274275
if (!sourcePath) {
275276
indicator.fail('No input file or folder specified.');
276277
process.exit(1);
277278
}
278279

279-
// Check source exists, is a dir or a file ?
280+
// Check a source exists, is a dir or a file?
280281
if (sourcePath !== '-' && !existsSync(sourcePath)) {
281282
indicator.fail('Input file or folder does not exist.');
282283
process.exit(1);
283284
}
284285
const sourceIsFile = (sourcePath !== '-') && lstatSync(sourcePath).isFile();
285286

286-
// Try to define type of target (file or dir).
287+
// Try to define the type of target (file or dir).
287288
// Assume the target is the same as the source
288289
let targetIsFile = sourceIsFile;
289290

@@ -301,28 +302,30 @@ async function main() {
301302
indicator.info('No sourceparser was specified. Input will be parsed as a GeoStyler object.');
302303
sourceFormat = 'geostyler';
303304
}
304-
const sourceParser = getParserFromFormat(sourceFormat);
305+
const sourceParserOptions = getParserOptions(sourceOptions);
306+
const sourceParser = getParserFromFormat(sourceFormat, sourceParserOptions);
305307

306-
// Get target parser.
308+
// Get the target parser.
307309
if (!targetFormat && targetIsFile) {
308310
targetFormat = getFormatFromFilename(outputPath);
309311
}
310312
if (!targetFormat) {
311313
indicator.info('No targetparser was specified. Output will be a GeoStyler object.');
312314
targetFormat = 'geostyler';
313315
}
314-
const targetParser = getParserFromFormat(targetFormat);
316+
const targetParserOptions = getParserOptions(targetOptions);
317+
const targetParser = getParserFromFormat(targetFormat, targetParserOptions);
315318

316-
// Get source(s) path(s).
319+
// Get the source(s) path(s).
317320
const sourcePaths = collectPaths(sourcePath, sourceIsFile);
318321

319322
const writePromises: Promise<number>[] = [];
320323
sourcePaths.forEach((srcPath) => {
321324
indicator.text = `Transforming ${srcPath} from ${sourceFormat} to ${targetFormat}`;
322-
// Get correct output path
325+
// Get a correct output path
323326
const outputPathFile = computeTargetPath(srcPath, outputPath, targetIsFile, targetFormat);
324327

325-
// Add the the translation promise.
328+
// Add the translation promise.
326329
writePromises.push(writeFile(srcPath, sourceParser, outputPathFile, targetParser, indicator));
327330
});
328331

src/logHelper.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const logTitle = () => {
1212
`));
1313
};
1414

15-
export const logHelp = () :void => {
15+
export const logHelp = (): void => {
1616
logTitle();
1717
console.log(`
1818
Basic syntax:
@@ -25,22 +25,25 @@ export const logHelp = () :void => {
2525
geostyler-cli -s sld -t qgis -o ./output-sld testdata/sld
2626
2727
Options:
28-
-h / --help : Display this help and exit.
29-
-o / --output : Output filename or directory. Required when the source is a directory.
30-
For a file leave this empty to write to stdout. [string]
31-
-s / --source : Source parser, either "mapbox", "mapfile" or "map",
32-
"sld" or "se" for SLD - the parser will read the version from the file,
33-
"qgis" or "qml" for QGIS QML files, and "ol-flat" for OpenLayers FlatStyles.
34-
If not given, it will be guessed from the extension of the input file.
35-
Mandatory if the the target is a directory.
36-
-t / --target : Target parser, either "mapbox", "sld" (for SLD 1.0), "se" (for SLD 1.1),
37-
"qgis" or "qml" for QGIS QML files, or "ol-flat" for OpenLayers FlatStyles.
38-
If not given, it will be guessed from the extension of the output file.
39-
Mapfiles are not currently supported as target.
40-
Mandatory if the the target is a directory.
41-
--from-stdin : Read input from stdin. If this option is set, the input file is ignored.
42-
-v / --version: Display the version of the program.
43-
--quiet : Suppress all interactive output.
28+
-h / --help : Display this help and exit.
29+
-o / --output : Output filename or directory. Required when the source is a directory.
30+
For a file leave this empty to write to stdout. [string]
31+
-s / --source : Source parser, either "mapbox", "mapfile", "map",
32+
"sld" - the parser will read the version from the file -
33+
"qgis" or "qml" for QGIS QML files, and "ol-flat" for OpenLayers FlatStyles.
34+
If not given, it will be guessed from the extension of the input file.
35+
Mandatory if the the target is a directory.
36+
-t / --target : Target parser, either "mapbox", "sld",
37+
"qgis" or "qml" for QGIS QML files, or "ol-flat" for OpenLayers FlatStyles.
38+
If not given, it will be guessed from the extension of the output file.
39+
Mapfiles are not currently supported as target.
40+
Mandatory if the the target is a directory.
41+
--sourceOptions: Options for the constructor of the source parser. This must
42+
be a comma-separated string like --sourceOptions 'version:1.1.0,debug:true'.
43+
--targetOptions: Same as sourceOptions, but for the target parser.
44+
--from-stdin : Read input from stdin. If this option is set, the input file is ignored.
45+
-v / --version : Display the version of the program.
46+
--quiet : Suppress all interactive output.
4447
`);
4548
};
4649

src/utils.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const parseOption = (value: string): boolean | number | string | undefined => {
2+
const asNumber = Number(value);
3+
if (!isNaN(asNumber)) {
4+
return asNumber;
5+
}
6+
const isTrue = value.toLowerCase() === 'true';
7+
if (isTrue) {
8+
return true;
9+
}
10+
const isFalse = value.toLowerCase() === 'false';
11+
if (isFalse) {
12+
return false;
13+
}
14+
return value;
15+
};
16+
17+
/**
18+
* Parses the option string into a (flat) object and parse boolean (false/true) and numeric values.
19+
* Expected input "key1:value1,key2:value2"
20+
*/
21+
export const getParserOptions = (optionsAsString: string): Record<string, unknown> => {
22+
if (!optionsAsString) {
23+
return {};
24+
}
25+
const options = optionsAsString.split(',');
26+
const optionsObject: Record<string, unknown> = {};
27+
options.forEach((option) => {
28+
const splitOption = option.split(':');
29+
if (splitOption.length === 2) {
30+
const optionName = splitOption[0].trim();
31+
const optionValue = splitOption[1].trim();
32+
optionsObject[optionName] = parseOption(optionValue);
33+
}
34+
});
35+
return optionsObject;
36+
};

test.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const fs = require('fs');
22
const { spawnSync } = require('child_process');
3+
const { getParserOptions } = require('./build/src/utils.js');
34

45
function checkFileCreated(outputFile) {
56
try {
@@ -22,6 +23,40 @@ function runTest(args, outputFile) {
2223
return result;
2324
}
2425

26+
function parseOptionsTest() {
27+
const result = getParserOptions('version:1.2.0,debug:true,silent:false,num:1.1');
28+
if (result.version !== '1.2.0') {
29+
return false;
30+
}
31+
if (result.debug !== true) {
32+
return false;
33+
}
34+
if (result.silent !== false) {
35+
return false;
36+
}
37+
return result.num === 1.1;
38+
}
39+
40+
function parseEmptyOptionsTest() {
41+
const isEmptyObject = (obj) => {
42+
return Object.keys(obj).length === 0 && obj.constructor === Object;
43+
};
44+
let result = getParserOptions(undefined);
45+
if (!isEmptyObject(result)) {
46+
return false;
47+
}
48+
result = getParserOptions(null);
49+
if (!isEmptyObject(result)) {
50+
return false;
51+
}
52+
result = getParserOptions('');
53+
if (!isEmptyObject(result)) {
54+
return false;
55+
}
56+
result = getParserOptions('true');
57+
return isEmptyObject(result);
58+
}
59+
2560
function runAllTests() {
2661

2762
let success = true;
@@ -117,6 +152,14 @@ function runAllTests() {
117152
success = false;
118153
}
119154

155+
// Test the parseOptions functions.
156+
if (!parseOptionsTest() || !parseEmptyOptionsTest()) {
157+
console.log('Parser options tests failed');
158+
success = false;
159+
} else {
160+
console.log('Parser options tests ok');
161+
}
162+
120163
return success;
121164
}
122165

0 commit comments

Comments
 (0)