diff --git a/index.js b/index.js index 4a2ad17..685be23 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,6 @@ 'use strict'; -require('./src/result._init.c'); require('./src/result.and.c'); require('./src/result.andThen.c'); require('./src/result.expect.c'); @@ -20,7 +19,11 @@ require('./src/result.or.c'); require('./src/result.orElse.c'); require('./src/result.registerGlobals.c'); require('./src/result.unwrap.c'); +require('./src/result.unwrapAnd.c'); +require('./src/result.unwrapAndThen.c'); require('./src/result.unwrapErr.c'); +require('./src/result.unwrapOr.c'); +require('./src/result.unwrapOrElse.c'); module.exports = require('./interface/result.h'); diff --git a/interface/result-sym.h.js b/interface/result-sym.h.js new file mode 100644 index 0000000..0efcca5 --- /dev/null +++ b/interface/result-sym.h.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = { + isOk: Symbol(), + value: Symbol(), +}; diff --git a/interface/result.h.js b/interface/result.h.js index 0cf3ce6..ee7927c 100644 --- a/interface/result.h.js +++ b/interface/result.h.js @@ -38,6 +38,15 @@ * @returns {*} */ +/** + * This Callback is used to produce a final Value + * + * @callback ValueEmitter + * @param {*} val + * `val` will contain the result of {Result}. + * @returns {*} + */ + /** * This Callback is used as a return-handler * @@ -60,17 +69,6 @@ * @type {Result} */ module.exports = class Result { - - /** - * Create new Result from either a value or an error - * Leave the one you don't need undefined - * You have to pass something to exactly one of the two - * - * @param {*} val - * @param {*} err - */ - constructor (val, err) { this._init(val, err); }; - /** * Create success Result with a return value. * @@ -141,40 +139,36 @@ module.exports = class Result { iter() { throw new Error ('Not Implemented: Result.iter'); }; /** - * Returns `val` if the result is Ok, otherwise returns the Err value of itself. + * Returns `Ok(val)` if the result is Ok, otherwise returns `Err(err)` of itself. * * @param {*} val - * @returns {*} + * @returns {Result} */ and(val) { throw new Error ('Not Implemented: Result.and'); }; /** - * Calls `resultEmitter` if the result is Ok, otherwise returns the Err value of itself. + * Calls `resultEmitter` if the result is Ok, otherwise returns `Err(err)` value of itself. * This function can be used for control flow based on Result values. * * @param {ResultEmitter} resultEmitter - * @returns {*} + * @returns {Result} */ andThen(resultEmitter) { throw new Error ('Not Implemented: Result.andThen'); }; /** - * Returns `val` if the result is Err, otherwise returns the Ok value of itself. - * Since there are no strict types in JS, this method and unwrapOr are identical. + * Returns `Ok(val)` if the result is Err, otherwise returns `Ok(ok)` of itself. * - * @alias unwrapOr * @param {*} val - * @returns {*} + * @returns {Result} */ or(val) { throw new Error ('Not Implemented: Result.or'); }; /** - * Calls `resultEmitter` if the result is Err, otherwise returns the Ok value of itself. + * Calls `resultEmitter` if the result is Err, otherwise returns the `Ok(ok)` value of itself. * This function can be used for control flow based on result values. - * Since there are no strict types in JS, this method and unwrapOrElse are identical. * - * @alias unwrapOrElse * @param {ResultEmitter} resultEmitter - * @returns {*} + * @returns {Result} */ orElse(resultEmitter) { throw new Error ('Not Implemented: Result.orElse'); }; @@ -186,6 +180,22 @@ module.exports = class Result { */ unwrap() { throw new Error ('Not Implemented: Result.unwrap'); }; + /** + * Unwraps a result, yielding the content of optb. Else it throws. + * + * @param {*} optb + * @return {*} + */ + unwrapAnd(optb) { throw new Error ('Not Implemented: Result.unwrapAnd'); }; + + /** + * Unwraps a result, calling valEmitter with its value. If the value is an Err then it throws. + * + * @param {ValueEmitter} valEmitter + * @return {*} + */ + unwrapAndThen(valEmitter) { throw new Error ('Not Implemented: Result.unwrapAndThen'); }; + /** * Unwraps a result, yielding the content of an Err. * @@ -194,6 +204,22 @@ module.exports = class Result { */ unwrapErr() { throw new Error ('Not Implemented: Result.unwrapErr'); }; + /** + * Unwraps a result, yielding the content of an Ok. Else it returns optb. + * + * @param {*} optb + * @return {*} + */ + unwrapOr(optb) { throw new Error ('Not Implemented: Result.unwrapOr'); }; + + /** + * Unwraps a result, yielding the content of an Ok. If the value is an Err then it calls valEmitter with its value. + * + * @param {ValueEmitter} valEmitter + * @return {*} + */ + unwrapOrElse(valEmitter) { throw new Error ('Not Implemented: Result.unwrapOrElse'); }; + /** * Unwraps a result, yielding the content of an Ok. * diff --git a/package.json b/package.json index f6a6bde..bf12ba9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "result-js", - "version": "3.0.2", + "version": "4.0.0", "description": "Rusty Monad Results for JS", "author": { "email": "me@marco-alka.de", diff --git a/src/result._init.c.js b/src/result._init.c.js deleted file mode 100644 index b59123f..0000000 --- a/src/result._init.c.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -require('../interface/result.h').prototype._init = function ($val, $err) { - - // Other objects in the prototype chain can set the following property - // in order to prevent default initialization which might overwrite - // previously set data - if (this._isInitialized) { - - return; - } - - if ( - (typeof $val === 'undefined' && typeof $err === 'undefined') || - ($val === null && $err === null) - ) { - - this._val = undefined; - this._err = new Error('Result cannot be initialized with neither `val` nor `err` passed to it!'); - } - else { - - this._val = $val === 0 ? 0 : $val || null; - - if (typeof $err === 'undefined' || $err === null) { - - this._err = null; - } - else { - - this._err = $err; - } - } - - this._isInitialized = true; -}; diff --git a/src/result.and.c.js b/src/result.and.c.js index c3ba243..22fd67d 100644 --- a/src/result.and.c.js +++ b/src/result.and.c.js @@ -1,6 +1,13 @@ 'use strict'; -require('../interface/result.h').prototype.and = function ($val) { +const H = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); - return this.isOk() ? $val : this._err; + +H.and = function ($val) { + if (this[sym.isOk]) { + this[sym.value] = $val; + } + + return this; }; diff --git a/src/result.andThen.c.js b/src/result.andThen.c.js index 9ddf17b..06fe46e 100644 --- a/src/result.andThen.c.js +++ b/src/result.andThen.c.js @@ -1,6 +1,13 @@ 'use strict'; -require('../interface/result.h').prototype.andThen = function ($resultEmitter) { +const H = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); - return this.isOk() ? $resultEmitter(this._val) : this._err; + +H.andThen = function ($resultEmitter) { + if (this[sym.isOk]) { + this[sym.value] = $resultEmitter(this[sym.value]); + } + + return this; }; diff --git a/src/result.expect.c.js b/src/result.expect.c.js index 17cd3e8..bdeb340 100644 --- a/src/result.expect.c.js +++ b/src/result.expect.c.js @@ -1,10 +1,13 @@ 'use strict'; -require('../interface/result.h').prototype.expect = function ($msg) { +const sym = require('../interface/result-sym.h'); - return this.orElse(() => { +require('../interface/result.h').prototype.expect = function ($msg) { + if (!this[sym.isOk]) { const msg = typeof $msg === 'string' ? $msg : 'ERROR: $msg is not a string. Also -> '; - throw new Error(`${msg}: ${this._err.toString()}`); - }); + throw new Error(`${msg}: ${this[sym.value].toString()}`); + } + + return this[sym.value]; }; diff --git a/src/result.expectErr.c.js b/src/result.expectErr.c.js index fa10d0b..707a22d 100644 --- a/src/result.expectErr.c.js +++ b/src/result.expectErr.c.js @@ -1,12 +1,13 @@ 'use strict'; -require('../interface/result.h').prototype.expectErr = function ($msg) { +const sym = require('../interface/result-sym.h'); - if (this.isErr()) { - return this._err; +require('../interface/result.h').prototype.expectErr = function ($msg) { + if (!this[sym.isOk]) { + return this[sym.value]; } const msg = typeof $msg === 'string' ? $msg : 'ERROR: $msg is not a string. Also -> '; - throw new Error(`${msg}: ${this._ok.toString()}`); + throw new Error(`${msg}: ${this[sym.value].toString()}`); }; diff --git a/src/result.fromError.c.js b/src/result.fromError.c.js index 20b8f43..5594f62 100644 --- a/src/result.fromError.c.js +++ b/src/result.fromError.c.js @@ -1,9 +1,14 @@ 'use strict'; -const h = require('../interface/result.h'); +const H = require('../interface/result.h'); +const sym = require('../interface/result-sym.h'); -h.fromError = function ($err) { +H.fromError = function ($err) { + const result = new H(); - return new h(undefined, $err); + result[sym.isOk] = false; + result[sym.value] = $err; + + return result; }; diff --git a/src/result.fromSuccess.c.js b/src/result.fromSuccess.c.js index f16891a..342400c 100644 --- a/src/result.fromSuccess.c.js +++ b/src/result.fromSuccess.c.js @@ -1,9 +1,14 @@ 'use strict'; -const h = require('../interface/result.h'); +const H = require('../interface/result.h'); +const sym = require('../interface/result-sym.h'); -h.fromSuccess = function ($val) { +H.fromSuccess = function ($val) { + const result = new H(); - return new h($val); + result[sym.isOk] = true; + result[sym.value] = $val; + + return result; }; diff --git a/src/result.fromTry.c.js b/src/result.fromTry.c.js index b686174..5c6c9fa 100644 --- a/src/result.fromTry.c.js +++ b/src/result.fromTry.c.js @@ -1,16 +1,20 @@ 'use strict'; -const h = require('../interface/result.h'); +const H = require('../interface/result.h'); +const sym = require('../interface/result-sym.h'); -h.fromTry = function ($fun) { +H.fromTry = function ($fun) { + const result = new H(); try { - - return h.fromSuccess($fun()); + result[sym.value] = $fun(); + result[sym.isOk] = true; } catch ($err) { - - return h.fromError($err); + result[sym.value] = $err; + result[sym.isOk] = false; } + + return result; }; diff --git a/src/result.isErr.c.js b/src/result.isErr.c.js index 82facf7..342b4fc 100644 --- a/src/result.isErr.c.js +++ b/src/result.isErr.c.js @@ -1,6 +1,8 @@ 'use strict'; -require('../interface/result.h').prototype.isErr = function () { +const sym = require('../interface/result-sym.h'); + - return this._err !== null; +require('../interface/result.h').prototype.isErr = function () { + return !this[sym.isOk]; }; diff --git a/src/result.isOk.c.js b/src/result.isOk.c.js index 5d15e09..20eb7c9 100644 --- a/src/result.isOk.c.js +++ b/src/result.isOk.c.js @@ -1,6 +1,8 @@ 'use strict'; -require('../interface/result.h').prototype.isOk = function () { +const sym = require('../interface/result-sym.h'); + - return this._err === null; +require('../interface/result.h').prototype.isOk = function () { + return !!this[sym.isOk]; }; diff --git a/src/result.iter.c.js b/src/result.iter.c.js index ea2530f..6c3d7a9 100644 --- a/src/result.iter.c.js +++ b/src/result.iter.c.js @@ -1,18 +1,16 @@ 'use strict'; const hProto = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); hProto.iter = function () { - const self = this; - return { + return { [Symbol.iterator]: function* () { - - if (self.isOk()) { - - yield self._val; + if (self[sym.isOk]) { + yield self[sym.value]; } }, }; diff --git a/src/result.map.c.js b/src/result.map.c.js index 9b27827..7c30129 100644 --- a/src/result.map.c.js +++ b/src/result.map.c.js @@ -2,16 +2,18 @@ const Result = require('../interface/result.h'); const hProto = Result.prototype; +const sym = require('../interface/result-sym.h'); hProto.map = function ($op) { + const op = typeof $op === 'function' + ? $op + : () => this[sym.value] + ; - if (typeof $op === 'undefined') $op = $ => $; - - if (this.isOk()) { - - return Result.fromSuccess($op(this._val)); + if (this[sym.isOk]) { + this[sym.value] = op(this[sym.value]); } - return Result.fromError(this._err); + return this; }; diff --git a/src/result.mapErr.c.js b/src/result.mapErr.c.js index 9b92f0a..5302fe9 100644 --- a/src/result.mapErr.c.js +++ b/src/result.mapErr.c.js @@ -2,16 +2,18 @@ const Result = require('../interface/result.h'); const hProto = Result.prototype; +const sym = require('../interface/result-sym.h'); hProto.mapErr = function ($op) { + const op = typeof $op === 'function' + ? $op + : () => this[sym.value] + ; - if (typeof $op === 'undefined') $op = $ => $; - - if (this.isErr()) { - - return Result.fromError($op(this._err)); + if (!this[sym.isOk]) { + this[sym.value] = op(this[sym.value]); } - return Result.fromSuccess(this._val); + return this; }; diff --git a/src/result.match.c.js b/src/result.match.c.js index 01c5226..9aa3cac 100644 --- a/src/result.match.c.js +++ b/src/result.match.c.js @@ -1,16 +1,14 @@ 'use strict'; const hProto = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); hProto.match = function ($okHandler, $errHandler) { - - if (this.isOk()) { - - $okHandler(this._val); + if (this[sym.isOk]) { + (typeof $okHandler === 'function') && $okHandler(this[sym.value]); } else { - - $errHandler(this._err); + (typeof $errHandler === 'function') && $errHandler(this[sym.value]); } }; diff --git a/src/result.node.c.js b/src/result.node.c.js index 592dc8f..263ef24 100644 --- a/src/result.node.c.js +++ b/src/result.node.c.js @@ -1,17 +1,18 @@ 'use strict'; const hProto = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); hProto.node = function ($handler) { - if (typeof $handler !== 'function') { - throw new Error('The handler must be callable!'); } - $handler( - this.isErr() ? this._err : null, - this.isOk() ? this._val : null - ); + if (this[sym.isOk]) { + $handler(null, this[sym.value]); + } + else { + $handler(this[sym.value]); + } }; diff --git a/src/result.or.c.js b/src/result.or.c.js index 584e4f1..c657304 100644 --- a/src/result.or.c.js +++ b/src/result.or.c.js @@ -1,11 +1,14 @@ 'use strict'; -const hProto = require('../interface/result.h').prototype +const hProto = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); hProto.or = function ($val) { + if (!this[sym.isOk]) { + this[sym.value] = $val; + this[sym.isOk] = true; + } - return this.isErr() ? $val : this._val; + return this; }; - -hProto.unwrapOr = hProto.or; diff --git a/src/result.orElse.c.js b/src/result.orElse.c.js index 262419e..eef0403 100644 --- a/src/result.orElse.c.js +++ b/src/result.orElse.c.js @@ -1,11 +1,14 @@ 'use strict'; -const hProto = require('../interface/result.h').prototype +const hProto = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); hProto.orElse = function ($resultEmitter) { + if (!this[sym.isOk]) { + this[sym.value] = $resultEmitter(this[sym.value]); + this[sym.isOk] = true; + } - return this.isErr() ? $resultEmitter(this._err) : this._val; + return this; }; - -hProto.unwrapOrElse = hProto.orElse; diff --git a/src/result.registerGlobals.c.js b/src/result.registerGlobals.c.js index e306586..6e1d7dc 100644 --- a/src/result.registerGlobals.c.js +++ b/src/result.registerGlobals.c.js @@ -3,16 +3,7 @@ const h = require('../interface/result.h'); -let initialized = false; - h.registerGlobals = function () { - - if (initialized) { - - return; - } - global.Ok = h.fromSuccess; global.Err = h.fromError; - initialized = true; }; diff --git a/src/result.unwrap.c.js b/src/result.unwrap.c.js index 110b986..6b11e3b 100644 --- a/src/result.unwrap.c.js +++ b/src/result.unwrap.c.js @@ -1,11 +1,12 @@ 'use strict'; -require('../interface/result.h').prototype.unwrap = function () { +const sym = require('../interface/result-sym.h'); - if (this.isOk()) { - return this._val; +require('../interface/result.h').prototype.unwrap = function () { + if (this[sym.isOk]) { + return this[sym.value]; } - throw new Error(this._err); + throw new Error(this[sym.value]); }; diff --git a/src/result.unwrapAnd.c.js b/src/result.unwrapAnd.c.js new file mode 100644 index 0000000..40e450f --- /dev/null +++ b/src/result.unwrapAnd.c.js @@ -0,0 +1,13 @@ +'use strict'; + +const H = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); + + +H.unwrapAnd = function ($val) { + if (this[sym.isOk]) { + return $val; + } + + throw new Error('The contained value is an Err!'); +}; diff --git a/src/result.unwrapAndThen.c.js b/src/result.unwrapAndThen.c.js new file mode 100644 index 0000000..0a7542a --- /dev/null +++ b/src/result.unwrapAndThen.c.js @@ -0,0 +1,13 @@ +'use strict'; + +const H = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); + + +H.unwrapAndThen = function ($resultEmitter) { + if (this[sym.isOk]) { + return $resultEmitter(this[sym.value]); + } + + throw new Error('The contained value is an Err!'); +}; diff --git a/src/result.unwrapErr.c.js b/src/result.unwrapErr.c.js index dab030b..a224a0b 100644 --- a/src/result.unwrapErr.c.js +++ b/src/result.unwrapErr.c.js @@ -1,11 +1,12 @@ 'use strict'; -require('../interface/result.h').prototype.unwrapErr = function () { +const sym = require('../interface/result-sym.h'); - if (this.isErr()) { - return this._err; +require('../interface/result.h').prototype.unwrapErr = function () { + if (!this[sym.isOk]) { + return this[sym.value]; } - throw new Error(this._ok); + throw new Error(this[sym.value]); }; diff --git a/src/result.unwrapOr.c.js b/src/result.unwrapOr.c.js new file mode 100644 index 0000000..a74084a --- /dev/null +++ b/src/result.unwrapOr.c.js @@ -0,0 +1,13 @@ +'use strict'; + +const hProto = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); + + +hProto.unwrapOr = function ($val) { + if (!this[sym.isOk]) { + return $val; + } + + return this[sym.value]; +}; diff --git a/src/result.unwrapOrElse.c.js b/src/result.unwrapOrElse.c.js new file mode 100644 index 0000000..9ded1fc --- /dev/null +++ b/src/result.unwrapOrElse.c.js @@ -0,0 +1,13 @@ +'use strict'; + +const hProto = require('../interface/result.h').prototype; +const sym = require('../interface/result-sym.h'); + + +hProto.unwrapOrElse = function ($resultEmitter) { + if (!this[sym.isOk]) { + return $resultEmitter(this[sym.value]); + } + + return this[sym.value]; +}; diff --git a/test.js b/test.js index 9453449..d0630c7 100755 --- a/test.js +++ b/test.js @@ -8,7 +8,6 @@ const TAP = require('tap'); TAP.test('Test interface', $t => { const _interface = require('./interface/result.h'); - require('./src/result._init.c'); const tmp = new _interface(''); const funs = Object.getOwnPropertyNames(_interface).concat(Object.getOwnPropertyNames(_interface.prototype)); @@ -17,7 +16,7 @@ TAP.test('Test interface', $t => { funs.forEach($fun => { - if ($fun === 'constructor' || $fun === '_init') { + if ($fun === 'constructor') { $t.pass($fun + ' is always defined'); return; @@ -46,91 +45,139 @@ const Result = require('.'); TAP.test('Create Results', $t => { - $t.plan(4); + $t.plan(3); $t.ok(Result.fromError('TEST'), 'Create Result from Error'); $t.ok(Result.fromSuccess('TEST'), 'Create Result from Success'); $t.ok((new Result()).isErr(), 'Create Result with neither val nor err'); - const tmp = new Result('TEST'); - tmp._init(); - $t.equal(tmp.unwrap(), 'TEST', 'Re-Initialization not possible'); - $t.end(); }); TAP.test('Success Tests', $t => { - $t.plan(15); - - const s = Result.fromSuccess('TEST'); - const s_try = Result.fromTry(() => 5); - - $t.ok(s.isOk(), 'Success.isOK'); - $t.notOk(s.isErr(), '!Success.notOk'); - - $t.equal(s.unwrap(), 'TEST', 'Check Success.unwrap'); - $t.throws(s.unwrapErr.bind(s), 'Check Ok.unwrapErr'); - $t.equal(s.expect(), 'TEST', 'Check Success.expect'); - $t.throws(s.expectErr.bind(s), 'Check Ok.expectErr'); - $t.equal(s.and('NYAN'), 'NYAN', 'Check Success.and'); - $t.equal(s.andThen($v => $v.toString() + '2'), 'TEST2', 'Check Success.andThen'); - $t.equal(s.or('FAIL'), 'TEST', 'Check Success.or'); - $t.equal(s.orElse($err => $err.toString() + '2'), 'TEST', 'Check Success.orElse'); - s.node((err, val) => { - $t.equal(err, null, 'Check Error.node err'); - $t.equal(val, 'TEST', 'Check Error.node val'); - }); + $t.plan(19); + + { + const s = Result.fromSuccess('TEST'); + + $t.ok(s.isOk(), 'Success.isOK'); + $t.notOk(s.isErr(), '!Success.notOk'); + + $t.equal(s.unwrap(), 'TEST', 'Check Success.unwrap'); + $t.throws(s.unwrapErr.bind(s), 'Check Ok.unwrapErr'); + $t.equal(s.expect(), 'TEST', 'Check Success.expect'); + $t.throws(s.expectErr.bind(s), 'Check Ok.expectErr'); + } + + { + const s = Result.fromSuccess('TEST'); + $t.equal(s.and('NYAN').unwrap(), 'NYAN', 'Check Success.and'); + } + + { + const s = Result.fromSuccess('TEST'); + $t.equal(s.andThen($v => $v.toString() + '2').unwrap(), 'TEST2', 'Check Success.andThen'); + } + + { + const s = Result.fromSuccess('TEST'); + $t.equal(s.unwrapAnd('NYAN'), 'NYAN', 'Check Success.unwrapAnd'); + $t.equal(s.unwrapAndThen($v => $v.toString() + '2'), 'TEST2', 'Check Success.unwrapAndThen'); + } + + { + const s = Result.fromSuccess('TEST'); + $t.equal(s.or('FAIL').unwrap(), 'TEST', 'Check Success.or'); + } + + { + const s = Result.fromSuccess('TEST'); + $t.equal(s.orElse($err => $err.toString() + '2').unwrap(), 'TEST', 'Check Success.orElse'); + } + + { + const s = Result.fromSuccess('TEST'); + $t.equal(s.unwrapOr('FAIL'), 'TEST', 'Check Success.unwrapOr'); + $t.equal(s.unwrapOrElse($err => $err.toString() + '2'), 'TEST', 'Check Success.unwrapOrElse'); + } + + { + const s = Result.fromSuccess('TEST'); + const s_try = Result.fromTry(() => 5); + + s.node((err, val) => { + $t.equal(err, null, 'Check Error.node err'); + $t.equal(val, 'TEST', 'Check Error.node val'); + }); + + s.match($val => { + $t.equal($val, 'TEST', 'Check Success.match'); + }, () => { + $t.fail('Check Success.match'); + }); + + $t.ok(s_try.isOk(), 'Try.isOk'); + $t.equal(s_try.or(-1).unwrap(), 5, 'Try value'); + } - s.match($val => { + $t.end(); +}); - $t.equal($val, 'TEST', 'Check Success.match'); - }, () => { +TAP.test('Error Tests', $t => { - $t.fail('Check Success.match'); - }); + $t.plan(17); - $t.ok(s_try.isOk(), 'Try.isOk'); - $t.equal(s_try.or(-1), 5, 'Try value'); + { + const e = Result.fromError('TEST'); - $t.end(); -}); + $t.notOk(e.isOk(), '!Error.isOK'); + $t.ok(e.isErr(), 'Error.notOk'); -TAP.test('Error Tests', $t => { + $t.throws(e.unwrap.bind(e), 'Check Error.unwrap'); + $t.equal(e.unwrapErr(), 'TEST', 'Check Error.unwrapErr'); + $t.throws(e.expect.bind(e, 'ERROR'), 'Check Error.expect'); + $t.equal(e.expectErr(), 'TEST', 'Check Error.expectErr'); + $t.throws(() => e.and('NYAN').unwrap(), 'Check Error.and'); + $t.throws(() => e.andThen($v => $v.toString() + '2').unwrap(), 'Check Error.andThen'); + $t.equal(e.or('FAIL').unwrap(), 'FAIL', 'Check Error.or'); + } - $t.plan(15); - - const e = Result.fromError('TEST'); - const e_try = Result.fromTry(() => { throw 'uh oh'; }); - - $t.notOk(e.isOk(), '!Error.isOK'); - $t.ok(e.isErr(), 'Error.notOk'); - - $t.throws(e.unwrap.bind(e), 'Check Error.unwrap'); - $t.equal(e.unwrapErr(), 'TEST', 'Check Error.unwrapErr'); - $t.throws(e.expect.bind(e, 'ERROR'), 'Check Error.expect'); - $t.equal(e.expectErr(), 'TEST', 'Check Error.expectErr'); - $t.equal(e.and('NYAN').toString(), 'TEST', 'Check Error.and'); - $t.equal(e.andThen($v => $v.toString() + '2').toString(), 'TEST', 'Check Error.andThen'); - $t.equal(e.or('FAIL'), 'FAIL', 'Check Error.or'); - $t.equal(e.orElse($err => $err.toString() + '2').toString(), 'TEST2', 'Check Error.orElse'); - e.node((err, val) => { - $t.equal(err, 'TEST', 'Check Error.node err'); - $t.equal(val, null, 'Check Error.node val'); - }); + { + const e = Result.fromError('TEST'); + $t.equal(e.orElse($err => $err.toString() + '2').unwrap(), 'TEST2', 'Check Error.orElse'); + } - e.match(() => { + { + const e = Result.fromError('TEST'); + const e_try = Result.fromTry(() => { + throw 'uh oh'; + }); - $t.fail('Check Error.match'); + e.node((err, val) => { + $t.equal(err, 'TEST', 'Check Error.node err'); + $t.equal(typeof val, 'undefined', 'Check Error.node val'); + }); - }, $err => { + e.match(() => { - $t.equal($err.toString(), 'TEST', 'Check Error.match'); - }); + $t.fail('Check Error.match'); - $t.ok(e_try.isErr(), 'Try.isErr'); - $t.equal(e_try.or(-1), -1, 'Try value'); + }, $err => { + + $t.equal($err.toString(), 'TEST', 'Check Error.match'); + }); + + $t.ok(e_try.isErr(), 'Try.isErr'); + $t.equal(e_try.or(-1).unwrap(), -1, 'Try value'); + } + + { + const e = Result.fromError('TEST'); + $t.throws(() => e.unwrapAnd(-1), 'Check Error.unwrapAnd'); + $t.equal(e.unwrapOr(-1), -1, 'Check Error.unwrapOr'); + } $t.end(); }); @@ -139,17 +186,17 @@ TAP.test('Control Flow Tests', $t => { $t.plan(7); - const sq = $x => Result.fromSuccess($x * $x); - const err = $x => Result.fromError($x); + const sq = $x => $x * $x; + const add = $x => $x + 1; - $t.equal(Result.fromSuccess(2).orElse(sq), 2, 'Success.orElse Test sq'); - $t.equal(Result.fromSuccess(2).andThen(sq).orElse(sq), 4, 'Success.andThen.orElse Test sq sq'); - $t.equal(Result.fromSuccess(2).andThen(err).or(13), 13, 'Success.andThen.orElse Test err sq'); - $t.equal(Result.fromSuccess(2).andThen(sq).orElse(err), 4, 'Success.andThen.orElse Test sq err'); - $t.equal(Result.fromSuccess(2).andThen(err).orElse(err).or(13), 13, 'Success.andThen.orElse.unwrap Test err err'); + $t.equal(Result.fromSuccess(2).unwrapOrElse(sq), 2, 'Success.orElse Test sq'); + $t.equal(Result.fromSuccess(2).andThen(sq).orElse(sq).unwrap(), 4, 'Success.andThen.orElse Test sq sq'); + $t.equal(Result.fromSuccess(2).andThen(add).unwrapOr(13), 3, 'Success.andThen.orElse Test err sq'); + $t.equal(Result.fromSuccess(2).andThen(sq).orElse(add).unwrap(), 4, 'Success.andThen.orElse Test sq err'); + $t.equal(Result.fromSuccess(2).andThen(add).orElse(add).or(13).unwrap(), 3, 'Success.andThen.orElse.unwrap Test err err'); $t.equal(Result.fromError(3).orElse(sq).unwrap(), 9, 'Error.orElse.unwrap Test sq'); - $t.equal(Result.fromError(3).andThen(sq).toString(), '3', 'Error.andThen Test sq'); + $t.equal(Result.fromError(3).andThen(sq).unwrapErr().toString(), '3', 'Error.andThen Test sq'); $t.end(); }); @@ -176,45 +223,63 @@ TAP.test('Misc Tests', $t => { $t.plan(8); - const good = Result.fromSuccess('test'); - const bad = Result.fromError('test'); + { + const good = Result.fromSuccess('test'); + const bad = Result.fromError('test'); - $t.equal(good.map().unwrap(), 'test', 'Default Result.map Test on Ok'); - $t.equal(good.map($val => $val + '_ok').unwrap(), 'test_ok', 'Result.map Test on Ok'); - bad.map($val => $val + '_ok').match(() => { + $t.equal(good.map().unwrap(), 'test', 'Default Result.map Test on Ok'); + $t.equal(good.map($val => $val + '_ok').unwrap(), 'test_ok', 'Result.map Test on Ok'); + bad.map($val => $val + '_ok').match(() => { - $t.fail('Result.map Test on Err'); - }, $e => { + $t.fail('Result.map Test on Err'); + }, $e => { - $t.equal($e, 'test', 'Result.map Test on Err'); - }); + $t.equal($e, 'test', 'Result.map Test on Err'); + }); + } - $t.equal(good.mapErr($val => $val + '_ok').unwrap(), 'test', 'Result.mapErr Test on Ok'); - bad.mapErr().match(() => { + { + const good = Result.fromSuccess('test'); + $t.equal(good.mapErr($val => $val + '_ok').unwrap(), 'test', 'Result.mapErr Test on Ok'); + } - $t.fail('Default Result.mapErr Test on Err'); - }, $e => { + { + const bad = Result.fromError('test'); - $t.equal($e, 'test', 'Default Result.mapErr Test on Err'); - }); - - bad.mapErr($val => $val + '_ok').match(() => { + bad.mapErr().match(() => { - $t.fail('Result.mapErr Test on Err'); - }, $e => { + $t.fail('Default Result.mapErr Test on Err'); + }, $e => { - $t.equal($e, 'test_ok', 'Result.mapErr Test on Err'); - }); + $t.equal($e, 'test', 'Default Result.mapErr Test on Err'); + }); + + bad.mapErr($val => $val + '_ok').match(() => { + + $t.fail('Result.mapErr Test on Err'); + }, $e => { + + $t.equal($e, 'test_ok', 'Result.mapErr Test on Err'); + }); + } + + { + const good = Result.fromSuccess('test'); + const goodIterArr = Array.from(good.iter()); + let c = 0; + + goodIterArr.forEach(() => c++); + $t.equal(c, 1, 'Ok.iter() Test'); + } - let c = 0; - const goodIterArr = Array.from(good.iter()); - goodIterArr.forEach(() => c++); - $t.equal(c, 1, 'Ok.iter() Test'); + { + const bad = Result.fromError('test'); + const badIterArr = Array.from(bad.iter()); + let c = 0; - c = 0; - const badIterArr = Array.from(bad.iter()); - badIterArr.forEach(() => c++); - $t.equal(c, 0, 'Err.iter() Test'); + badIterArr.forEach(() => c++); + $t.equal(c, 0, 'Err.iter() Test'); + } $t.end(); });