diff --git a/README.md b/README.md index 7f2ee72..e3f49ef 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,12 @@ cfg.toXML(); // output as a XML string. or from a command line: ``` to-json.js input.xml -to-yaml input.json -to-xml ipnut.yml +to-yaml.js input.json +to-xml.js ipnut.yml +``` +or via STDIN +``` +to-json.js < input.xml +to-yaml.js < input.json +to-xml.js < ipnut.yml ``` diff --git a/bin/to-json.js b/bin/to-json.js index 2e3e1bc..8791f54 100755 --- a/bin/to-json.js +++ b/bin/to-json.js @@ -4,6 +4,16 @@ const Path = require('path'); const Configuration = require(Path.join(__dirname, '..', 'configuration.js')); let input = process.argv[2]; -let cfg = Configuration.fromFile(input); +let cfg; + +if (input) { + // a filename argument was given. + cfg = Configuration.fromFile(input); + +} else { + // no arguments given so read STDIN. + cfg = Configuration.fromSTDIN(); + +} process.stdout.write(cfg.toJSON()); diff --git a/bin/to-xml.js b/bin/to-xml.js index 9ca5b63..861e945 100755 --- a/bin/to-xml.js +++ b/bin/to-xml.js @@ -4,6 +4,16 @@ const Path = require('path'); const Configuration = require(Path.join(__dirname, '..', 'configuration.js')); let input = process.argv[2]; -let cfg = Configuration.fromFile(input); +let cfg; + +if (input) { + // a filename argument was given. + cfg = Configuration.fromFile(input); + +} else { + // no arguments given so read STDIN. + cfg = Configuration.fromSTDIN(); + +} process.stdout.write(cfg.toXML()); diff --git a/bin/to-yaml.js b/bin/to-yaml.js index 853040c..c3bce7f 100755 --- a/bin/to-yaml.js +++ b/bin/to-yaml.js @@ -4,6 +4,16 @@ const Path = require('path'); const Configuration = require(Path.join(__dirname, '..', 'configuration.js')); let input = process.argv[2]; -let cfg = Configuration.fromFile(input); +let cfg; + +if (input) { + // a filename argument was given. + cfg = Configuration.fromFile(input); + +} else { + // no arguments given so read STDIN. + cfg = Configuration.fromSTDIN(); + +} process.stdout.write(cfg.toYAML()); diff --git a/configuration.js b/configuration.js index 2eed13b..aaa3e18 100644 --- a/configuration.js +++ b/configuration.js @@ -8,14 +8,20 @@ const XML = require('fast-xml-parser'); const libPath = Path.join(__dirname, 'lib'); const Parsers = require(Path.join(libPath, 'parsers.js')); +const STDIN = require(Path.join(libPath, 'stdin.js')); class Configuration extends Object { - constructor(input) { + constructor(raw, type) { super(); - Object.assign(this, input); + let parser = Parsers[type]; + let inpStr = raw.toString(); + + let content = parser.parse(inpStr); + + Object.assign(this, content); } toJSON() { @@ -41,10 +47,27 @@ class Configuration extends Object { let type = MIME.lookup(filename); let raw = File.readFileSync(filename); - let parser = Parsers[type]; - let content = parser.parse(raw.toString()); + return new this(raw, type); + } + + static fromSTDIN() { + let raw = this.STDIN.get(); + let type; + + if (Parsers.isJSON(raw)) { + type = MIME.lookup('json'); + } else if (Parsers.isYAML(raw)) { + type = MIME.lookup('yml'); + } else if (Parsers.isXML(raw)) { + type = MIME.lookup('xml'); + } + + return new this(raw, type); + } - return new this(content); + static get STDIN() { + // exposed for testability/stubbing. + return STDIN; } } diff --git a/lib/parsers.js b/lib/parsers.js index e145a67..9bba889 100644 --- a/lib/parsers.js +++ b/lib/parsers.js @@ -6,6 +6,25 @@ const Parsers = { 'text/yaml': YAML, 'application/json': JSON, 'application/xml': XML, + + isJSON: function(str) { + let match = str.match(/^\{[\{\}\[\]\"\'\w\s\:\,]+[\}\s]$/gi); + + return !!match && match.toString() === str; + }, + + isYAML: function(str) { + let match = str.match(/[\w\s\:\[\]^\{^\}^\<^\>\,\-]+/gi); + + return !!match && match.toString() === str; + }, + + isXML: function(str) { + let match = str.match(/^[\<\>\w\s\/\=\'\"]+$/gi); + + return !!match && match.toString() === str; + }, + }; diff --git a/lib/stdin.js b/lib/stdin.js new file mode 100644 index 0000000..f3350e5 --- /dev/null +++ b/lib/stdin.js @@ -0,0 +1,37 @@ + +const File = require('fs'); + + +class STDIN { + + static get BUFSIZE() { + return this._bufSize || 256; + } + + static set BUFSIZE(val) { + return this._bufSize = val; + } + + static get() { + let len = 0; + let contents = ''; + let file = process.stdin.fd; + + let bufSize = this.BUFSIZE; + let buf = new Buffer(bufSize); + + do { + len = File.readSync(file, buf, 0, bufSize, null); + + let str = buf.toString(); + contents += str.slice(0, len); + + } while (len > 0); + + return contents; + } + +} + + +module.exports = STDIN; diff --git a/spec/configuration_spec.js b/spec/configuration_spec.js index afaa759..e4c4138 100644 --- a/spec/configuration_spec.js +++ b/spec/configuration_spec.js @@ -1,8 +1,12 @@ const Path = require('path'); +const File = require('fs'); + const YAML = require('yaml'); const XML = require('fast-xml-parser'); +const MIME = require('mime-types'); + const specPath = Path.join(__dirname); const rootPath = Path.join(specPath, '..'); const supportPath = Path.join(specPath, 'support'); @@ -24,7 +28,7 @@ describe(Configuration.name, () => { instance = Configuration.fromFile(jsonStub); }); - describe('the toJSON() function.', () => { + describe('the toJSON() function', () => { beforeEach(() => { result = instance.toJSON(); }); @@ -41,7 +45,7 @@ describe(Configuration.name, () => { }); - describe('the toXML() function.', () => { + describe('the toXML() function', () => { beforeEach(() => { result = instance.toXML(); }); @@ -58,7 +62,7 @@ describe(Configuration.name, () => { }); - describe('the toYAML() function.', () => { + describe('the toYAML() function', () => { beforeEach(() => { result = instance.toYAML(); }); @@ -157,4 +161,102 @@ describe(Configuration.name, () => { }); + describe('the fromSTDIN() static function', () => { + let input; + let stub; + let promise = Promise.resolve(); + let file; + + beforeEach(() => { + let baseLib = Configuration.STDIN; + + input = File.readFileSync(stub); + + spyOn(baseLib, 'get') + .and.returnValue(input.toString(), MIME.lookup(stub)); + }); + + describe('given JSON input', () => { + stub = jsonStub; + + it('parses the given input object', () => { + let res = Configuration.fromSTDIN(); + let obj = res.anObject; + + expect(obj.one).toEqual(1); + expect(obj.two).toEqual(2); + expect(obj.three).toEqual(3); + expect(obj.four).toEqual(4); + + }); + + it('parses the given input array', () => { + let res = Configuration.fromSTDIN(); + let ary = res.anArray; + + expect(ary[0]).toEqual(1); + expect(ary[1]).toEqual(2); + expect(ary[2]).toEqual(3); + expect(ary[3]).toEqual(4); + + }); + + }); + + describe('given YAML input', () => { + stub = yamlStub; + + it('parses the given input object', () => { + let res = Configuration.fromSTDIN(); + let obj = res.anObject; + + expect(obj.one).toEqual(1); + expect(obj.two).toEqual(2); + expect(obj.three).toEqual(3); + expect(obj.four).toEqual(4); + + }); + + it('parses the given input array', () => { + let res = Configuration.fromSTDIN(); + let ary = res.anArray; + + expect(ary[0]).toEqual(1); + expect(ary[1]).toEqual(2); + expect(ary[2]).toEqual(3); + expect(ary[3]).toEqual(4); + + }); + + }); + + describe('given XML input', () => { + stub = xmlStub; + + it('parses the given input object', () => { + let res = Configuration.fromSTDIN(); + let obj = res.anObject; + + expect(obj.one).toEqual(1); + expect(obj.two).toEqual(2); + expect(obj.three).toEqual(3); + expect(obj.four).toEqual(4); + + }); + + it('parses the given input array', () => { + let res = Configuration.fromSTDIN(); + let ary = res.anArray; + + expect(ary[0]).toEqual(1); + expect(ary[1]).toEqual(2); + expect(ary[2]).toEqual(3); + expect(ary[3]).toEqual(4); + + }); + + }); + + }); + }); diff --git a/spec/lib/parsers_spec.js b/spec/lib/parsers_spec.js new file mode 100644 index 0000000..c60b39d --- /dev/null +++ b/spec/lib/parsers_spec.js @@ -0,0 +1,89 @@ + +const Path = require('path'); +const File = require('fs'); + +const specPath = Path.join(__dirname, '..'); +const rootPath = Path.join(specPath, '..'); +const supportPath = Path.join(specPath, 'support'); + +const xmlStub = Path.join(supportPath, 'input.xml'); +const yamlStub = Path.join(supportPath, 'input.yml'); +const jsonStub = Path.join(supportPath, 'input.json'); + +const Parsers = require(Path.join(rootPath, 'lib', 'parsers.js')); + + +describe(Parsers.name, () => { + let content = { + json: File.readFileSync(jsonStub).toString(), + yaml: File.readFileSync(yamlStub).toString(), + xml: File.readFileSync(xmlStub).toString(), + }; + + describe('the isJSON() static function', () => { + + it('returns true given valid JSON data', () => { + let result = Parsers.isJSON(content.json); + + expect(result).toBe(true); + }); + + it('returns false given valid YAML data', () => { + let result = Parsers.isJSON(content.yaml); + + expect(result).toBe(false); + }); + + it('returns false given valid XML data', () => { + let result = Parsers.isJSON(content.xml); + + expect(result).toBe(false); + }); + + }); + + describe('the isYAML() static function', () => { + + it('returns false given valid JSON data', () => { + let result = Parsers.isYAML(content.json); + + expect(result).toBe(false); + }); + + it('returns true given valid YAML data', () => { + let result = Parsers.isYAML(content.yaml); + + expect(result).toBe(true); + }); + + it('returns false given valid XML data', () => { + let result = Parsers.isYAML(content.xml); + + expect(result).toBe(false); + }); + + }); + + describe('the isXML() static function', () => { + + it('returns false given valid JSON data', () => { + let result = Parsers.isXML(content.json); + + expect(result).toBe(false); + }); + + it('returns false given valid YAML data', () => { + let result = Parsers.isXML(content.yaml); + + expect(result).toBe(false); + }); + + it('returns true given valid XML data', () => { + let result = Parsers.isXML(content.xml); + + expect(result).toBe(true); + }); + + }); + +});