var _ = require('underscore') ; var when; // type Callback = status:Number -> result:Any -> Either HTTPResult Any // type Handler = { f:Callback, next:HTTPResult } // :: status:Number? -> result:Any? -> HTTPResult var counter = 0; function HTTPResult(status,result) { this.id = counter++; this.status = 0; this.result = null; this._handlers = []; if ( status ) { this.set(status,result); } } // isSet :: Bool HTTPResult.prototype.isSet = function() { return this.status !== 0; }; // set :: status:Number -> result:Any? -> () HTTPResult.prototype.set = function(status,result) { if ( this.isSet() ) { throw new Error("Result was already set."); } if ( !_.isNumber(status) ) { throw new Error("Status must be a number."); } this.status = status; this.result = result; _.each(this._handlers,this._fire.bind(this)); }; // _fire :: Handler -> () HTTPResult.prototype._fire = function(handler) { try { var ret = handler.f(this.status,this.result); if ( ret instanceof HTTPResult ) { ret.handle(handler.next.set.bind(handler.next)); } else { handler.next.set(this.status,ret); } } catch(ex) { console.log('HTTPResult','exception',ex.toString(),ex.stack); handler.next.set(-1,ex); } }; /* handle :: Either Callback * {status:Number*: (result -> Either HTTPResult Any), * 'default'?: Callback} * -> HTTPResult */ HTTPResult.prototype.handle = function(fOrObj) { var f = _.isFunction(fOrObj) ? fOrObj : function(status,result) { if ( status in fOrObj ) { return fOrObj[status](result); } else if ( status >= 200 && status < 300 && 'success' in fOrObj ) { return fOrObj.success(status,result); } else if ( !(status >= 200 && status < 300) && 'failure' in fOrObj ) { return fOrObj.failure(status,result); } else if ( 'default' in fOrObj ) { return fOrObj['default'](status,result); } else { return result; } }; var next = new HTTPResult(); var handler = { f: f, next: next }; if ( this.isSet() ) { this._fire(handler); } else { this._handlers.push(handler); } return next; }; // then :: onSuccess:(result:Any -> status:Number)? -> // onError:(result:Any -> status:Number)? -> HTTPResult HTTPResult.prototype.then = function(onSuccess,onError) { var f = function(status,result) { if ( status >= 200 && status < 300 && onSuccess ) { if ( onSuccess ) { result = onSuccess(result,status); } } else { if ( onError ) { result = onError(result,status); } } return result; }; return this.handle(f); }; HTTPResult.prototype.asCallback = function(status) { status = status || 200; return function(ex,result) { if ( ex ) { this.set(-1,ex); } else { this.set(status,result); } }.bind(this); }; // when :: Either HTTPResult Any -> HTTPResult when = HTTPResult.when = function(status,valueOrResult) { status = status || 200; if ( valueOrResult instanceof HTTPResult ) { return valueOrResult; } else { return new HTTPResult(status,valueOrResult); } }; // ok :: result:Any? -> HTTPResult HTTPResult.ok = function(result) { return new HTTPResult(200,result); }; // fail :: error:Any? -> HTTPResult HTTPResult.fail = function(error) { return new HTTPResult(-1,error); }; module.exports = HTTPResult;