<commit_before>/** f_ module dependencies
 * Mainly colorized logging related
 */
var Ezlog = require('Ezlog'),
		cls = require('opensoars_cls'),
		log = new Ezlog({pref:{t:'[f_]',c:'yellow'}}),
		logGreen = new Ezlog({pref:{t:'[f_]',c:'yellow'},text:{c:'green'}}),
		logRed = new Ezlog({pref:{t:['[f_]'],c:'yellow'},text: {c:'red'}});

/** Color helper functions
 * Makes cls calls in code unnecessary
 * @arg    t {string}  The text to color
 * @return   {string}  The colorized text
 */
function GREEN(t){  return cls({c: 'green',  t: t            }); }
function RED(t){    return cls({c: 'red',    t: t            }); }
function BLUE(t){   return cls({c: 'blue',   t: t, s: 'bold' }); }
function YELLOW(t){ return cls({c: 'yellow', t: t, s: 'bold' }); }
function WHITE(t){  return cls({c: 'white',  t: t            }); }

var f_methods = {

	next: function next(){

		if(this.f_i === 0 && this.f_hasInitialized === false){
			this.f_startTime = Date.now(); 
			this.f_hasInitialized = true;
		}


		if(this.f_i === 0 && this.f_toLog.has('start'))
			log('Starting task '
			+ (this.f_desc ? BLUE(' ' + this.f_desc) : '')
			+ '  Attempt ' + GREEN(this.f_retries.all + 1)
			+ ' of max ' + RED(this.f_maxRetries.all+ 1) + ' attempts');


		// Are we done with functionFlow? If yes finish, else continue
		if(this.f_i === this.f_functionFlow.length)
			return this.f_finish();

		// Get methodName from functionFlow array
		var methodName = this.f_functionFlow[this.f_i];

		// Is the method actualy there? And is it a function ?
		if(this[methodName] && typeof this[methodName] === 'function'){

			if(this.f_toLog.has('next')) log('next: ' + YELLOW(methodName));

			this.f_i += 1;

			// When onNext is present, send it the currunt method and the f_ i(ndex)
			if(this.onNext) this.onNext({ method: methodName, f_i: this.f_i });

			return this[methodName]();
		}
		// If we can't call another function from functionFlow, ABORT!
		else return this.f_abort('no next function in functionFlow');

	},

	/** Simple namespace reset function
	 * Will be further edited to allow deeper object namespace keeping
	 */
	resetNamespace: function resetNamespace(){
		this[this.f_dataNamespace] = {};
	},

	retryAll: function retryAll(desc, err){

		if(this.f_toLog.has('retry')){
			log( RED('Retry') + (desc ? '  desc: ' + RED(desc) : '')
				+ (err  ? '  err: '  + RED(err)  : '') );
			console.log('');
		}
		
		this.f_addErr(desc, err);

		this.f_retries.all += 1;

		// Are we exceeding the maxRetries.all ?
		if(this.f_retries.all > this.f_maxRetries.all)
			return this.f_abort('this.f_maxRetries.all > this.f_retries.all');

		// Do we want a data namespace reset ?
		if(this.f_resetOnRetryAll)
			this.f_resetNamespace();

		this.f_i = 0;

		if(this.onRetryAll) this.onRetryAll();

		return this.f_next();
	},

	retryMethod: function retryMethod(){},
	retryFrom: function retryFrom(){},
	retryThis: function retryThis(){},

	/** Helper function to add errors to f_errs array
	 * @desc {string} error description
	 * @err  {object} node error
	 */
	addErr: function addErr(desc, err){
		this.f_errs.push({ desc: (desc || ''), err: (err || {}) });
	},

	/** Error stack logger helper function
	 * Pretty prints the error stack
	 * Gets called from f_abort if wanted
	 */
	logErrStack: function logErrStack(){
		console.log(''); logRed('- - START f_errs - -');
		for(var i=0; i<this.f_errs.length; i+=1)
			console.log(this.f_errs[i]);
		logRed('- - END   f_errs - -'); console.log('');
	},

	/** f_abort function
	 * @desc {string} error description
	 * @err  {object} node error
	 * Calls f_logErrStack if wanted
	 */
	abort: function abort(desc, err){
		this.f_endTime = Date.now();
		this.f_timeTaken = this.f_endTime - this.f_startTime;

		this.f_addErr('Abort, desc: ' + (desc || ''), (err  || {}));

		if(this.f_toLog.has('abort')){
			logRed('ABORT' + (desc ? ', desc: ' + WHITE(desc) : ''));
			logRed("Time 'wasted': " + this.f_timeTaken.toString() + " ms");
			logRed('f_errs stack below');
			this.f_logErrStack();
		}

		if(this.onAbort) return this.onAbort();
	},


	/** When we are done with f_ task this method gets called
	 * Calculate the time taken to complete the task
	 */
	finish: function finish(){

		this.f_endTime = Date.now();
		this.f_timeTaken = this.f_endTime - this.f_startTime;

		if(this.f_toLog.has('finish'))
			log(GREEN('finish') + '  time taken: '
				+ GREEN(this.f_timeTaken.toString()) + ' ms');

		if(this.onFinish) return this.onFinish();
	}

};

/** All log triggers
 * These are added to f_toLog when ['all'] is specified
 */
var f_toLogTriggers = ['start', 'next', 'retry', 'error', 'abort', 'finish'];


/**
 * Use this function to add properties/methods to the prototype object.
 * We want these to be shared between instances, saves some overhead.
 * @arg    toAugment {func/instance}
 * @return toAugment {func/instance}
 */
function augment(toAugment, config){
	toAugment = toAugment || undefined;

	// Throw is used since we cannot do anything with f_ if reqs aren't met!

	if(!toAugment)
		throw '!toAugment Class|Object given';

	if(typeof toAugment !== 'function')
		throw 'wrong toAugment type, !function';

	if(!config)
		throw '!config  f_ needs config object';

	if(typeof config !== 'object')
		throw 'wrong config type, !object';

	if(!config.functionFlow)
		throw '!config.functionFlow, f_ needs to know which methods to call';

	if(!config.functionFlow.length)
		throw 'wrong config.functionFlow type, !array OR no method names given';


	// Can't be looped, we need defaults!
	var f_props = {
		errorArray: config.errorArray || 'errs',
		dataNamespace: config.dataNamespace || 'd',

		maxRetries: config.maxRetries || { all : 10 },

		// HAS TO BE EDITTED
		maxMethodRetries: config.maxMethodRetries || 10,


		maxMethodRetriesByName: config.maxMethodRetriesByName || {},
		toLog: config.toLog || ['all'],
		functionFlow: config.functionFlow || [],

		desc: config.desc || '',

		resetOnRetryAll: config.resetOnRetryAll || false
	};


	/**
	 * Make a copy of toAugment.prototype, store new properties in there.
	 * Later we asign it back to toAugment its real prototype object
	 */
	var toAugmentProto = toAugment.prototype;


	// Add methods to Class it's prototype object
	for(var methodName in f_methods)
		toAugmentProto['f_' + methodName] = f_methods[methodName];
	
	// Add properties to Class it's prototype object
	for(var propName in f_props)
		toAugmentProto['f_' + propName] = f_props[propName];


	/**
	 * Helper functions which shorten f_toLog indexOf logic
	 * @arg    checkFor {string}<commit_msg>Made sure maxRetries.all is always specified<commit_after>/** f_ module dependencies
 * Mainly colorized logging related
 */
var Ezlog = require('Ezlog'),
		cls = require('opensoars_cls'),
		log = new Ezlog({pref:{t:'[f_]',c:'yellow'}}),
		logGreen = new Ezlog({pref:{t:'[f_]',c:'yellow'},text:{c:'green'}}),
		logRed = new Ezlog({pref:{t:['[f_]'],c:'yellow'},text: {c:'red'}});

/** Color helper functions
 * Makes cls calls in code unnecessary
 * @arg    t {string}  The text to color
 * @return   {string}  The colorized text
 */
function GREEN(t){  return cls({c: 'green',  t: t            }); }
function RED(t){    return cls({c: 'red',    t: t            }); }
function BLUE(t){   return cls({c: 'blue',   t: t, s: 'bold' }); }
function YELLOW(t){ return cls({c: 'yellow', t: t, s: 'bold' }); }
function WHITE(t){  return cls({c: 'white',  t: t            }); }

var f_methods = {

	next: function next(){

		if(this.f_i === 0 && this.f_hasInitialized === false){
			this.f_startTime = Date.now(); 
			this.f_hasInitialized = true;
		}


		if(this.f_i === 0 && this.f_toLog.has('start'))
			log('Starting task '
			+ (this.f_desc ? BLUE(' ' + this.f_desc) : '')
			+ '  Attempt ' + GREEN(this.f_retries.all + 1)
			+ ' of max ' + RED(this.f_maxRetries.all+ 1) + ' attempts');


		// Are we done with functionFlow? If yes finish, else continue
		if(this.f_i === this.f_functionFlow.length)
			return this.f_finish();

		// Get methodName from functionFlow array
		var methodName = this.f_functionFlow[this.f_i];

		// Is the method actualy there? And is it a function ?
		if(this[methodName] && typeof this[methodName] === 'function'){

			if(this.f_toLog.has('next')) log('next: ' + YELLOW(methodName));

			this.f_i += 1;

			// When onNext is present, send it the currunt method and the f_ i(ndex)
			if(this.onNext) this.onNext({ method: methodName, f_i: this.f_i });

			return this[methodName]();
		}
		// If we can't call another function from functionFlow, ABORT!
		else return this.f_abort('no next function in functionFlow');

	},

	/** Simple namespace reset function
	 * Will be further edited to allow deeper object namespace keeping
	 */
	resetNamespace: function resetNamespace(){
		this[this.f_dataNamespace] = {};
	},

	retryAll: function retryAll(desc, err){

		if(this.f_toLog.has('retry')){
			log( RED('Retry') + (desc ? '  desc: ' + RED(desc) : '')
				+ (err  ? '  err: '  + RED(err)  : '') );
			console.log('');
		}
		
		this.f_addErr(desc, err);

		this.f_retries.all += 1;

		// Are we exceeding the maxRetries.all ?
		if(this.f_retries.all > this.f_maxRetries.all)
			return this.f_abort('this.f_maxRetries.all > this.f_retries.all');

		// Do we want a data namespace reset ?
		if(this.f_resetOnRetryAll)
			this.f_resetNamespace();

		this.f_i = 0;

		if(this.onRetryAll) this.onRetryAll();

		return this.f_next();
	},

	retryMethod: function retryMethod(){},
	retryFrom: function retryFrom(){},
	retryThis: function retryThis(){},

	/** Helper function to add errors to f_errs array
	 * @desc {string} error description
	 * @err  {object} node error
	 */
	addErr: function addErr(desc, err){
		this.f_errs.push({ desc: (desc || ''), err: (err || {}) });
	},

	/** Error stack logger helper function
	 * Pretty prints the error stack
	 * Gets called from f_abort if wanted
	 */
	logErrStack: function logErrStack(){
		console.log(''); logRed('- - START f_errs - -');
		for(var i=0; i<this.f_errs.length; i+=1)
			console.log(this.f_errs[i]);
		logRed('- - END   f_errs - -'); console.log('');
	},

	/** f_abort function
	 * @desc {string} error description
	 * @err  {object} node error
	 * Calls f_logErrStack if wanted
	 */
	abort: function abort(desc, err){
		this.f_endTime = Date.now();
		this.f_timeTaken = this.f_endTime - this.f_startTime;

		this.f_addErr('Abort, desc: ' + (desc || ''), (err  || {}));

		if(this.f_toLog.has('abort')){
			logRed('ABORT' + (desc ? ', desc: ' + WHITE(desc) : ''));
			logRed("Time 'wasted': " + this.f_timeTaken.toString() + " ms");
			logRed('f_errs stack below');
			this.f_logErrStack();
		}

		if(this.onAbort) return this.onAbort();
	},


	/** When we are done with f_ task this method gets called
	 * Calculate the time taken to complete the task
	 */
	finish: function finish(){

		this.f_endTime = Date.now();
		this.f_timeTaken = this.f_endTime - this.f_startTime;

		if(this.f_toLog.has('finish'))
			log(GREEN('finish') + '  time taken: '
				+ GREEN(this.f_timeTaken.toString()) + ' ms');

		if(this.onFinish) return this.onFinish();
	}

};

/** All log triggers
 * These are added to f_toLog when ['all'] is specified
 */
var f_toLogTriggers = ['start', 'next', 'retry', 'error', 'abort', 'finish'];


/**
 * Use this function to add properties/methods to the prototype object.
 * We want these to be shared between instances, saves some overhead.
 * @arg    toAugment {func/instance}
 * @return toAugment {func/instance}
 */
function augment(toAugment, config){
	toAugment = toAugment || undefined;

	// Throw is used since we cannot do anything with f_ if reqs aren't met!

	if(!toAugment)
		throw '!toAugment Class|Object given';

	if(typeof toAugment !== 'function')
		throw 'wrong toAugment type, !function';

	if(!config)
		throw '!config  f_ needs config object';

	if(typeof config !== 'object')
		throw 'wrong config type, !object';

	if(!config.functionFlow)
		throw '!config.functionFlow, f_ needs to know which methods to call';

	if(!config.functionFlow.length)
		throw 'wrong config.functionFlow type, !array OR no method names given';


	// Can't be looped, we need defaults!
	var f_props = {
		errorArray: config.errorArray || 'errs',
		dataNamespace: config.dataNamespace || 'd',

		maxRetries: config.maxRetries || { all : 10 },

		maxMethodRetriesByName: config.maxMethodRetriesByName || {},
		toLog: config.toLog || ['all'],
		functionFlow: config.functionFlow || [],

		desc: config.desc || '',

		resetOnRetryAll: config.resetOnRetryAll || false
	};


	if(!f_props.maxRetries.all) f_props.maxRetries.all = 10;

	/**
	 * Make a copy of toAugment.prototype, store new properties in there.
	 * Later we asign it back to toAugment its real prototype object
	 */
	var toAugmentProto = toAugment.prototype;


	// Add methods to Class it's prototype object
	for(var methodName in f_methods)
		toAugmentProto['f_' + methodName] = f_methods[methodName];
	
	// Add properties to Class it's prototype object
	for(var propName in f_props)
		toAugmentProto['f_' + propName] = f_props[propName];


	/**
	 * Helper functions which shorten f_toLog indexOf logic
	 * @arg    checkFor {string}
{'fraction_non_alphanumeric': 0.15810667071873574, 'fraction_numerical': 0.001975383680291749, 'mean_word_length': 3.692691622103387, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1141494', 'n_tokens_mistral': 4922, 'n_tokens_neox': 4532, 'n_words': 1226}