/**********************************************************************
* 
* stoppable.js
*
* Utility library implementing tooling to make stoppable functions...
*
*
* Repo and docs:
* 	https://github.com/flynx/stoppable.js
*
*
***********************************************/ /* c8 ignore next 2 */
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/


//---------------------------------------------------------------------
// helpers...

var AsyncFunction =
	(async function(){}).constructor

var Generator = 
	(function*(){}).constructor

var AsyncGenerator =
	(async function*(){}).constructor



//---------------------------------------------------------------------

// Wrap a callable in a STOP handler
//
// 	stoppable(func)
// 		-> func
//
// 	stoppable(gen)
// 		-> gen
//
// 	stoppable(asyncgen)
// 		-> asyncgen
//
//
// The client callable can be one of:
// 	- function
// 	- generator
// 	- async generator
//
// The returned callable will be of the same type as the input callable.
//
// The wrapper handles STOP slightly differently if the client is a 
// function or if it is a generator / async generator:
// 	- function
// 		STOP returned / thrown
// 			-> return undefined
// 		STOP(value) returned / thrown
// 			-> return value
// 	- generator / async generator
// 		STOP yielded / thrown
// 			-> iteration stops
// 		STOP(value) yielded / thrown
// 			-> value yielded and iteration stops
//
//
// NOTE: this repeats the same code at lest twice, not sure yet how to avoid 
// 		this...
module =
function(func){
	return Object.assign(
		// NOTE: the below implementations are almost the same, the main 
		// 		differences being the respective generator/async mechanics...
		func instanceof Generator ?
			function*(){
				try{
					for(var res of func.call(this, ...arguments)){
						if(res === module.STOP){
							return }
						if(res instanceof module.STOP){
							yield res.value
							return }
						yield res }
				} catch(err){
					if(err === module.STOP){
						return
					} else if(err instanceof module.STOP){
						yield err.value
						return }
					throw err } }
		: func instanceof AsyncGenerator ?
			async function*(){
				try{
					for await(var res of func.call(this, ...arguments)){
						if(res === module.STOP){
							return }
						if(res instanceof module.STOP){
							yield res.value
							return }
						yield res }
				} catch(err){
					if(err === module.STOP){
						return
					} else if(err instanceof module.STOP){
						yield err.value
						return }
					throw err } }
		: func instanceof AsyncFunction ?
			async function(){
				try{
					var res = await func.call(this, ...arguments)
					// NOTE: this is here for uniformity...
					if(res === module.STOP){
						return }
					if(res instanceof module.STOP){
						return res.value }
					return res
				} catch(err){
					if(err === module.STOP){
						return
					} else if(err instanceof module.STOP){
						return err.value }
					throw err } }
		: function(){
			try{
				var res = func.call(this, ...arguments)
				// NOTE: this is here for uniformity...
				if(res === module.STOP){
					return }
				if(res instanceof module.STOP){
					return res.value }
				return res
			} catch(err){
				if(err === module.STOP){
					return
				} else if(err instanceof module.STOP){
					return err.value }
				throw err } },
		{ toString: function(){
			return func.toString() }, }) }



//---------------------------------------------------------------------

module.STOP = 
function(value){
	return {
		__proto__: module.STOP.prototype,
		doc: 'stop iteration.',
		value,
	} }



/**********************************************************************
* vim:set ts=4 sw=4 nowrap :                        */ return module })
