pyconf.js/index.js

185 lines
3.9 KiB
JavaScript

'use strict';
var fs = require('fs');
var sfs = require('safe-replace').create();
function snakeCase(key) {
// TODO let user supply list of exceptions
if ('tlsSni01Port' === key) {
return 'tls_sni_01_port';
}
/*
else if ('http01Port' === key) {
return 'http01-port';
}
*/
else {
return key.replace(/([A-Z])/g, '_$1').toLowerCase();
}
}
function uc(match, c) {
return c.toUpperCase();
}
function camelCase(key) {
return key.replace(/_([a-z0-9])/g, uc);
}
function parsePythonConf(str, cb) {
var keys = {};
var obj = {};
var lines = str.split('\n');
lines.forEach(function (line, i) {
line = line.replace(/#.*/, '').trim();
if (!line) { return; }
var parts = line.split('=');
var pykey = parts.shift().trim();
var key = camelCase(pykey);
var val = parts.join('=').trim();
if ('True' === val) {
val = true;
}
else if ('False' === val) {
val = false;
}
else if ('None' === val || '' === val) {
val = null;
}
else if (/,/.test(val) && !/^"[^"]*"$/.test(val)) {
val = val.split(',').map(function(x) { return x.trim(); });
}
else if (/^-?[0-9]+$/.test(val)) {
val = parseInt(val, 10);
}
obj[key] = val;
if ('undefined' !== typeof keys[key]) {
console.warn("unexpected duplicate key '" + key + "': '" + val + "'");
}
keys[key] = i;
});
// we want to be able to rewrite the file with comments, etc
obj.__keys = keys;
obj.__lines = lines;
cb(null, obj);
}
function toPyVal(val) {
if (null === val || '' === val) {
return 'None';
}
else if (true === val) {
return 'True';
}
else if (false === val) {
return 'False';
}
else if ('string' === typeof val) {
return val;
}
else if ('number' === typeof val) {
return val;
}
else if (Array.isArray(val)) {
val = val.join(',');
if (-1 === val.indexOf(',')) {
val += ','; // disambiguates value from array with one element
}
return val;
}
return val && JSON.stringify(val);
}
function stringifyPythonConf(obj, cb) {
var endline;
// nix the final newline
if (!obj.__lines[obj.__lines.length - 1].trim()) {
endline = obj.__lines.pop();
}
Object.keys(obj).forEach(function (key) {
if ('__' === key.slice(0, 2)) {
return;
}
var pykey = snakeCase(key);
var pyval = toPyVal(obj[key]);
var num = obj.__keys[key];
var comment = '';
if ('undefined' === typeof pyval) {
if ('number' === typeof num) {
pyval = 'None';
} else {
return;
}
}
if ('number' !== typeof num) {
obj.__lines.push(pykey + ' = ' + pyval);
obj.__keys[key] = obj.__lines.length - 1;
return;
}
if ('[' === pykey[0]) {
return;
}
if (!obj.__lines[num] || !obj.__lines[num].indexOf) {
console.warn('[pyconf] WARN index past array length:');
console.log(obj.__lines.length, num, obj.__lines[num]);
return;
}
// restore comments
if (-1 !== obj.__lines[num].indexOf('#')) {
comment = obj.__lines[num].replace(/.*?(\s*#.*)/, '$1');
}
obj.__lines[num] = pykey + ' = ' + pyval + comment;
});
if ('string' === typeof endline) {
obj.__lines.push(endline);
}
cb(null, obj.__lines.join('\n'));
}
function writePythonConfFile(pathname, obj, cb) {
// TODO re-read file?
stringifyPythonConf(obj, function (err, text) {
sfs.writeFileAsync(pathname, text, 'utf8').then(function () {
cb(null, null);
}, function (err) {
cb(err);
});
});
}
function parsePythonConfFile(pathname, cb) {
fs.readFile(pathname, 'utf8', function (err, text) {
if (err) {
cb(err);
return;
}
parsePythonConf(text, cb);
});
}
module.exports.parse = parsePythonConf;
module.exports.readFile = parsePythonConfFile;
module.exports.stringify = stringifyPythonConf;
module.exports.writeFile = writePythonConfFile;