Skip to content
This repository was archived by the owner on Sep 8, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ INSERT INTO books VALUES('O''Reilly')
- `%L` quotes the argument value as an SQL literal. A null value is displayed as the string NULL, without quotes.
- `%%` In addition to the format specifiers described above, the special sequence %% may be used to output a literal % character.

Also all other "advanced" features (postition, flag, width) of the `format` function are implemented,
see [the Postgresql documentation](http://www.postgresql.org/docs/current/static/functions-string.html#FUNCTIONS-STRING-FORMAT).

# License

MIT
MIT
95 changes: 88 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

var assert = require('assert');
var fs = require('fs');
var XRegExp = require('xregexp').XRegExp;

/**
* Reserved word map.
Expand All @@ -16,6 +17,85 @@ var reserved = txt.split('\n').reduce(function(map, word){
return map;
}, {});

/**
* setup the regex stuff/lib
*/

var RegExpMain = (function(){
var reLib = {};
reLib.posInt = /[1-9][0-9]*/;
reLib.star = /\*/;
reLib.nth = XRegExp.build('{{star}}(?<widthNth>{{posInt}})[$]', reLib);
reLib.flag = XRegExp('(?<flag>[-])');
reLib.position = XRegExp.build('(?<position>{{posInt}})[$]', reLib);
reLib.width = XRegExp.build('(?<widthInt>{{posInt}})|(?<widthStar>{{star}})|{{nth}}', reLib);
reLib.type = XRegExp('(?<type>[sIL])');

var reMain = XRegExp.build('%((?<percent>%)|{{position}}?{{flag}}?{{width}}?{{type}})', reLib, 'g');

return reMain;
})();

/**
* Helper class for argument handling
*
* @param {Array} args
* @api private
*/
function Args(args) {
var curIndex = 1;

this.get = function(idx) {
curIndex = idx ? parseInt(idx, 10) : curIndex;
assert(curIndex > 0, 'Minimum usable index is 1');
return args[curIndex++];
};
};

/**
* get the current argument depending on all the format magic
*
* @param {Args} args
* @param {Object} matches
* @return {String}
* @api private
*/

function getArg(args, matches) {
var width = matches.widthInt;
var spacesOnLeft = matches.flag !== '-';
var pos = matches.position || null;
var arg = args.get(pos);

if (matches.widthStar === '*') {
width = arg;
arg = args.get(pos);
}

if (matches.widthNth) {
var argIdx = parseInt(matches.widthNth, 10);
width = args.get(argIdx);
arg = args.get(pos);
}

width = parseInt(width, 10);

if (width < 0) {
width *= -1;
if (spacesOnLeft) {
spacesOnLeft = !spacesOnLeft;
}
}

var numSpaces = width - arg.length;
if (numSpaces > 0) {
var spaces = (new Array(numSpaces+1)).join(' ');
arg = spacesOnLeft ? spaces.concat(arg) : arg.concat(spaces);
}

return arg;
}

/**
* Expose `format()`.
*/
Expand All @@ -32,13 +112,14 @@ exports = module.exports = format;
*/

function format(fmt) {
var i = 1;
var args = arguments;
return fmt.replace(/%([%sIL])/g, function(_, type){
if ('%' == type) return '%';
var args = new Args(arguments);

var arg = args[i++];
switch (type) {
return XRegExp.replace(fmt, RegExpMain, function(matches, type){
if (matches.percent !== undefined) return '%';

var arg = getArg(args, matches);

switch (matches.type) {
case 's': return exports.string(arg);
case 'I': return exports.ident(arg);
case 'L': return exports.literal(arg);
Expand Down Expand Up @@ -112,4 +193,4 @@ function validIdent(id) {
function quoteIdent(id) {
id = id.replace(/"/g, '""');
return '"' + id + '"';
}
}
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
"escape",
"query"
],
"dependencies": {},
"dependencies": {
"xregexp": "^2.0.0"
},
"devDependencies": {
"mocha": "*",
"should": "*"
},
"license": "MIT"
}
}
54 changes: 54 additions & 0 deletions test/advanced.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
var assert = require('assert');
var escape = require('..');

describe('advanced escape(fmt, ...)', function(){
it('should handle |%10s|', function(){
escape('|%10s|', 'foo')
.should.equal('| foo|');
});

it('should handle |%-10s|', function(){
escape('|%-10s|', 'foo')
.should.equal('|foo |');
});

it('should handle |%*s|', function(){
escape('|%*s|', 10, 'foo')
.should.equal('| foo|');
});

it('should handle |%*s|', function(){
escape('|%*s|', -10, 'foo')
.should.equal('|foo |');
});

it('should handle |%-*s|', function(){
escape('|%-*s|', 10, 'foo')
.should.equal('|foo |');
});

it('should handle |%-*s|', function(){
escape('|%-*s|', -10, 'foo')
.should.equal('|foo |');
});

it('should handle %3$s, %2$s, %1$s', function(){
escape('Testing %3$s, %2$s, %1$s', 'one', 'two', 'three')
.should.equal('Testing three, two, one');
});

it('should handle |%*2$s|', function(){
escape('|%*2$s|', 'foo', 10, 'bar')
.should.equal('| bar|');
});

it('should handle |%1$*2$s|', function(){
escape('|%1$*2$s|', 'foo', 10, 'bar')
.should.equal('| foo|');
});

it('should handle advanced position', function(){
escape('Testing %3$s, %2$s, %s', 'one', 'two', 'three')
.should.equal('Testing three, two, three');
});
});