diff --git a/README.md b/README.md index 532f0c6..5eeccc6 100644 --- a/README.md +++ b/README.md @@ -2,32 +2,48 @@ Transforms fixed string to object and vice versa -## Javascript +Is available in both functional and object oriented styles, for backwards compatibility. Although we recommend using the functional style. -```javascript -var transformer = new fixedstr([ - fixedstr.str('foo', 2), - fixedstr.str('bar', 5), - fixedstr.number('baz', 3) -]) +## Functional style + +Given the object definitions, you can either use currying to objectify/stringify, or use a factory function that curries for you. + +### Define objects + +Object definitions can be created using a helper function, that takes in a property name and length of property value + +```typescript +const objectDefinitions: IObjectDefinition[] = [ + stringObject('foo', 2), + stringObject('bar', 5), + numberObject('baz', 3) +] +``` + +### Currying method + +```typescript +curriedObjectify(objectDefinitions)('F Bar 012') // {foo: 'F', bar: 'Bar', baz: 12} +curriedstringify(objectDefinitions)({ foo: 'F', bar: 'Bar', baz: 12 }) // 'F Bar 012' +``` + +### Factory method + +```typescript +const transformer = createFixedStr(objectDefinitions) transformer.objectify('F Bar 012') // {foo: 'F', bar: 'Bar', baz: 12} transformer.stringify({ foo: 'F', bar: 'Bar', baz: 12 }) // 'F Bar 012' ``` -## Typescript +## Object oriented style ```typescript import { FixedStr } from 'fixedstr' -interface ITestObject { - foo: string - bar: string - bas: number -} const transformer = new FixedStr([ FixedStr.str('foo', 2), FixedStr.str('bar', 5), FixedStr.number('baz', 3) ]) -transformer.objectify('F Bar 012') // {foo: 'F', bar: 'Bar', baz: 12}:ITestObject +transformer.objectify('F Bar 012') // {foo: 'F', bar: 'Bar', baz: 12} transformer.stringify({ foo: 'F', bar: 'Bar', baz: 12 }) // 'F Bar 012' ``` diff --git a/package-lock.json b/package-lock.json index 0283947..5ff3ebd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "fixedstr", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -28,6 +28,12 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "arg": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.2.tgz", + "integrity": "sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -76,6 +82,12 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -179,6 +191,12 @@ "type-detect": "^4.0.0" } }, + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -399,6 +417,12 @@ "yallist": "^2.1.2" } }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, "mkdirp": { "version": "0.5.1", "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -697,6 +721,22 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "spdx-correct": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", @@ -756,6 +796,19 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "ts-node": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.2.tgz", + "integrity": "sha512-W1DK/a6BGoV/D4x/SXXm6TSQx6q3blECUzd5TN+j56YEMX3yPVMpHsICLedUw3DvGF3aTQ8hfdR9AKMaHjIi+A==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + } + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -911,6 +964,12 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 0ab7fd2..35bebce 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "fixedstr", "author": "Tryggingamiðstöðin ", - "version": "1.0.3", - "description": "Transforms fixed string to object and vice versa", + "version": "1.1.0", + "description": "Transforms fixed string to object and vice versa", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ @@ -26,12 +26,13 @@ "object", "object to string", "objectify" - ], + ], "scripts": { "lint": "tslint ./src/**", "format-verify": "prettier -l ./src/**", "format": "prettier --write ./src/**", "test": "mocha dist/test", + "tdd": "mocha --exit -R min --require ts-node/register --recursive --watch-extensions ts,js --watch 'src/test.ts'", "compile": "tsc", "clean": "rm -rf ./dist && tsc", "ci": "npm run clean && npm run lint && npm run format-verify && npm run test" @@ -46,7 +47,8 @@ "prettier": "^1.14.3", "tslint": "^5.11.0", "tslint-config-prettier": "^1.15.0", - "typescript": "^3.1.3" + "typescript": "^3.1.3", + "ts-node": "^8.5.2" }, "husky": { "hooks": { diff --git a/src/index.ts b/src/index.ts index 834d3e9..ef01cfd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,42 +21,87 @@ export interface IFixedStr { stringify: (obj: object) => string } -export class FixedStr implements IFixedStr { - public static str(name: string, size: number): IObjectDefinition { - return { - name: name, - size: size +export function stringObject(name: string, size: number): IObjectDefinition { + return { + name: name, + size: size + } +} +export function numberObject(name: string, size: number): IObjectDefinition { + return { + name: name, + size: size, + parse: Number, + toFixedString: (field, value) => { + const strVal = value && value.toString ? value.toString() : '0' + const pad = new Array(Math.max(0, field.size + 1 - strVal.length)).join( + '0' + ) + return pad + strVal } } - - public static strTrunc(name: string, size: number): IObjectDefinition { - return { - name: name, - size: size, - toFixedString: (field, value) => { - const str = (value || '').substring(0, size) - return textToString(field, str) - } +} +export function stringTruncObject( + name: string, + size: number +): IObjectDefinition { + return { + name: name, + size: size, + toFixedString: (field, value) => { + const str = (value || '').substring(0, size) + return textToString(field, str) } } - - public static number(name: string, size: number): IObjectDefinition { - return { - name: name, - size: size, - parse: Number, - toFixedString: (field, value) => { - const strVal = value && value.toString ? value.toString() : '0' - const pad = new Array(Math.max(0, field.size + 1 - strVal.length)).join( - '0' +} +export function curriedObjectify(objectDefinitions: IObjectDefinition[]) { + return (str: string = ''): TargetObject => { + let from = 0 + str = str || '' + return objectDefinitions.reduce( + (obj, field) => { + const parse = field.parse || parseText + obj[field.name] = parse(str.substring(from, from + field.size)) + from += field.size + return obj + }, + {} as TargetObject + ) + } +} +export function curriedStringify(objectDefinitions: IObjectDefinition[]) { + return (obj: any) => { + return objectDefinitions.reduce((str, field) => { + const toStr = field.toFixedString || textToString + const strValue = toStr(field, obj[field.name]) + if (strValue && strValue.length > field.size) { + throw new Error( + 'truncation error on field: ' + + field.name + + ', size: ' + + field.size + + ', value: ' + + obj[field.name] ) - return pad + strVal } - } + return str + strValue + }, '') + } +} +export function createFixedStr(objectDefinitions: IObjectDefinition[]) { + return { + objectify: curriedObjectify(objectDefinitions), + stringify: curriedStringify(objectDefinitions) } +} +export class FixedStr implements IFixedStr { + public static str = stringObject - private objDef: IObjectDefinition[] + public static strTrunc = stringTruncObject + public static number = numberObject + + private objDef: IObjectDefinition[] constructor(ObjectDefinitions: IObjectDefinition[]) { this.objDef = ObjectDefinitions } diff --git a/src/test.ts b/src/test.ts index cb3db60..7dda5a4 100644 --- a/src/test.ts +++ b/src/test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai' -import { FixedStr } from './' +import { createFixedStr, FixedStr, numberObject, stringObject } from './' interface ITestObject { foo: string @@ -8,103 +8,124 @@ interface ITestObject { } describe('fixedstr', () => { - const transformer = new FixedStr([ - FixedStr.str('foo', 2), + const tests = [ { - name: 'bar', - size: 5, - parse: str => str + description: 'object oriented style', + transformer: new FixedStr([ + FixedStr.str('foo', 2), + { + name: 'bar', + size: 5, + parse: str => str + }, + FixedStr.number('baz', 3) + ]) }, - FixedStr.number('baz', 3) - ]) - - it('should objectify', () => { - expect(transformer.objectify('F Bar 3')).to.eql({ - foo: 'F', - bar: 'Bar ', - baz: 3 - }) - }) - - it('should objectify empty string', () => { - expect(transformer.objectify('')).to.eql({ - foo: '', - bar: '', - baz: 0 - }) - }) + { + description: 'functional style', + transformer: createFixedStr([ + stringObject('foo', 2), + { + name: 'bar', + size: 5, + parse: str => str + }, + numberObject('baz', 3) + ]) + } + ] + tests.map(test => { + describe(test.description, () => { + const transformer = test.transformer + it('should objectify', () => { + expect(transformer.objectify('F Bar 3')).to.eql({ + foo: 'F', + bar: 'Bar ', + baz: 3 + }) + }) - it('should objectify empty undefined', () => { - expect(transformer.objectify()).to.eql({ - foo: '', - bar: '', - baz: 0 - }) - }) + it('should objectify empty string', () => { + expect(transformer.objectify('')).to.eql({ + foo: '', + bar: '', + baz: 0 + }) + }) - it('should stringify', () => { - expect( - transformer.stringify({ - foo: 'F', - bar: 'Bar', - baz: 3 + it('should objectify empty undefined', () => { + expect(transformer.objectify()).to.eql({ + foo: '', + bar: '', + baz: 0 + }) }) - ).to.equal('F Bar 003') - }) - it('should stringify missing fields', () => { - expect( - transformer.stringify({ - foo: 'F' + it('should stringify', () => { + expect( + transformer.stringify({ + foo: 'F', + bar: 'Bar', + baz: 3 + }) + ).to.equal('F Bar 003') }) - ).to.equal('F 000') - }) - it('should throw truncation error on string type', () => { - let ex - try { - transformer.stringify({ - foo: 'Fooo' + it('should stringify missing fields', () => { + expect( + transformer.stringify({ + foo: 'F' + }) + ).to.equal('F 000') }) - } catch (e) { - ex = e - } - expect(ex.message).to.contain('truncation error on field: foo') - }) - it('should throw truncation error on number type', () => { - let ex - try { - transformer.stringify({ - baz: 12345 + it('should throw truncation error on string type', () => { + let ex + try { + transformer.stringify({ + foo: 'Fooo' + }) + } catch (e) { + ex = e + } + expect(ex.message).to.contain('truncation error on field: foo') }) - } catch (e) { - ex = e - } - expect(ex.message).to.contain('truncation error on field: baz') - }) - it('should not throw truncation error if toFixedString truncated the value', () => { - const t = new FixedStr([ - { - name: 'foo', - size: 4, - toFixedString: (_, value) => { - return value.substr(0, 4) + it('should throw truncation error on number type', () => { + let ex + try { + transformer.stringify({ + baz: 12345 + }) + } catch (e) { + ex = e } - } - ]) - const str = t.stringify({ - foo: '123456' - }) - expect(str).to.equal('1234') - }) + expect(ex.message).to.contain('truncation error on field: baz') + }) - it('should not throw truncation error if using fixedstr.strTrunc', () => { - const t = new FixedStr([FixedStr.strTrunc('TEST', 5)]) - const str = t.stringify({ - TEST: '123456' + it('should not throw truncation error if toFixedString truncated the value', () => { + const t = new FixedStr([ + { + name: 'foo', + size: 4, + toFixedString: (_, value) => { + return value.substr(0, 4) + } + } + ]) + const str = t.stringify({ + foo: '123456' + }) + expect(str).to.equal('1234') + }) + + it('should not throw truncation error if using fixedstr.strTrunc', () => { + const t = new FixedStr([FixedStr.strTrunc('TEST', 5)]) + const str = t.stringify({ + TEST: '123456' + }) + expect(str).to.equal('12345') + }) }) - expect(str).to.equal('12345') }) })