<commit_before>"use strict";

var Types = require('../types');
var Validators = require('../validators');

/**
 * Defines a property that a Structure or Document can have
 */
class Property {
  constructor(def, name) {
    if (!def.type) {
      console.error(`${name} definition failed.`);
      console.error(def);
      throw new Error('Type is required for Property definition');
    }

    this._type = def.type;
    this._hydrate = def.hydrate ? true : false;
    this._array = def.array ? true : false;
    this._validators = def.validators || {};
    this._secure = def.secure || null;
    this._name = name;
  }


  /**
   * Validates one property of this model and returns the Validation Result
   * @param doc the object to validate
   * @param property a string of the property name to validate
   * @param method create, update or patch
   * @returns {*} a Validation Result object
   */
  validate(doc, method) {
    var self = this;

    //We will start by assuming this is valid.  If any one fails, this will be false.
    var valid = true;

    function _validate(val) {
      var result = {};

      for (var name in self.validators) {
        //Handled previously
        if (name === 'Required') {
          continue;
        }

        //This is a special validator just for Arrays, skip it here
        if (name.indexOf('Array') === 0) {
          continue;
        }

        var validator = Validators[name];

        //If the validatorName does not match a registered function in Validators, it is either a custom function
        //or a string representing a custom function that has not been compiled yet.
        if (!validator) {
          //This is a custom function definition.
          if (typeof(self.validators[name]) === 'function') {
            result = self.validators[name](val, doc, self.name);
          } else if (typeof(self.validators[name]) === 'string') {
            //Compile the function.
            //TODO: compile the validator function.
          } else {
            throw new Error(`Property ${self.name} has invalid validator ${name}.`);
          }
        } else {
          //Run the validator, passing in the value set in the property definition.
          result = validator(val, self.validators[name]);
        }


        if (result[name] === false) {
          valid = false;
        }
      }


      return result;
    }

    //Early exit. No validators? Then this field is valid by default.
    if (!this.validators) {
      return true;
    }

    var result = {};

    //Required is a special validator.
    //If a field is not required, it can be null and therefore passes validation, since null is valid.
    //Also, if the method is an Patch update, a field is not required for patch updates, because the required
    //field may already be set in the databse, so null is valid.
    if (this.validators.Required && (method === 'create' || method === 'update')) {
      result.Required = Validators.Required(doc[this.name]);

      //If there is no value in this field (ie. Required failed). then exit early. No need to run other validators.
      if (!result.Required) {
        return result;
      }
    } else {
      //If there is no Required validator set. Then we are going to use this Validator to check if a value is set.
      //If there is no value set, we can't run the rest of the validators (because there is no value).
      //However, this field is valid, because if it is not required a non-value is valid.

      //If this is a Patch update.  Then even though the field may be Required, it is not required at this moment
      if (!Validators.Required(doc[this.name])) {
        return true;
      }
    }

    //If this property is an array, we need to run validators on each of the array. And also run special array
    //validators on the array itself.
    if (this.isArray) {
      result = [];

      for (var name in this.validators) {
        if (!Validators[name]) {
          throw new Error(`Invalid Validator on ${this.name}:${name}`);
        }

        //This is a special validator just for Arrays, run it here
        result[name] = Validators[name](doc[this.name], this.validators[name]);

        if (result[name] !== true) {
          valid = false;
        }
      }

      for (let i = 0; i < doc[this.name].length; i++) {
        result[i] = _validate(method, doc[this.name][i]);
      }
    } else {
      result = _validate(method, doc[this.name])
    }

    //We don't care about the specific validation results if the property is valid.
    return valid ? true : result;
  }

  /**
   * Casts an value being set to the Type as defined by the property definition.
   * @param value the value to set this property to.
   * @param obj the parent object that this property is being set on
   * @returns {*}
   */
  cast (value, obj) {
    function subcast(val, obj, inst) {<commit_msg>Fixing error in validation for special Array validators<commit_after>"use strict";

var Types = require('../types');
var Validators = require('../validators');

/**
 * Defines a property that a Structure or Document can have
 */
class Property {
  constructor(def, name) {
    if (!def.type) {
      console.error(`${name} definition failed.`);
      console.error(def);
      throw new Error('Type is required for Property definition');
    }

    this._type = def.type;
    this._hydrate = def.hydrate ? true : false;
    this._array = def.array ? true : false;
    this._validators = def.validators || {};
    this._secure = def.secure || null;
    this._name = name;
  }


  /**
   * Validates one property of this model and returns the Validation Result
   * @param doc the object to validate
   * @param property a string of the property name to validate
   * @param method create, update or patch
   * @returns {*} a Validation Result object
   */
  validate(doc, method) {
    var self = this;

    //We will start by assuming this is valid.  If any one fails, this will be false.
    var valid = true;

    function _validate(val) {
      var result = {};

      for (var name in self.validators) {
        //Handled previously
        if (name === 'Required') {
          continue;
        }

        //This is a special validator just for Arrays, skip it here
        if (name.indexOf('Array') === 0) {
          continue;
        }

        var validator = Validators[name];

        //If the validatorName does not match a registered function in Validators, it is either a custom function
        //or a string representing a custom function that has not been compiled yet.
        if (!validator) {
          //This is a custom function definition.
          if (typeof(self.validators[name]) === 'function') {
            result = self.validators[name](val, doc, self.name);
          } else if (typeof(self.validators[name]) === 'string') {
            //Compile the function.
            //TODO: compile the validator function.
          } else {
            throw new Error(`Property ${self.name} has invalid validator ${name}.`);
          }
        } else {
          //Run the validator, passing in the value set in the property definition.
          result = validator(val, self.validators[name]);
        }


        if (result[name] === false) {
          valid = false;
        }
      }


      return result;
    }

    //Early exit. No validators? Then this field is valid by default.
    if (!this.validators) {
      return true;
    }

    var result = {};

    //Required is a special validator.
    //If a field is not required, it can be null and therefore passes validation, since null is valid.
    //Also, if the method is an Patch update, a field is not required for patch updates, because the required
    //field may already be set in the databse, so null is valid.
    if (this.validators.Required && (method === 'create' || method === 'update')) {
      result.Required = Validators.Required(doc[this.name]);

      //If there is no value in this field (ie. Required failed). then exit early. No need to run other validators.
      if (!result.Required) {
        return result;
      }
    } else {
      //If there is no Required validator set. Then we are going to use this Validator to check if a value is set.
      //If there is no value set, we can't run the rest of the validators (because there is no value).
      //However, this field is valid, because if it is not required a non-value is valid.

      //If this is a Patch update.  Then even though the field may be Required, it is not required at this moment
      if (!Validators.Required(doc[this.name])) {
        return true;
      }
    }

    //If this property is an array, we need to run validators on each of the array. And also run special array
    //validators on the array itself.
    if (this.isArray) {
      result = [];

      for (var name in this.validators) {
        if (!Validators[name]) {
          throw new Error(`Invalid Validator on ${this.name}:${name}`);
        }

        //This is a special validator just for Arrays, run it here
        if (name.indexOf('Array') === 0) {
          result[name] = Validators[name](doc[this.name], this.validators[name]);

          if (result[name] !== true) {
            valid = false;
          }
        }
      }

      for (let i = 0; i < doc[this.name].length; i++) {
        result[i] = _validate(method, doc[this.name][i]);
      }
    } else {
      result = _validate(method, doc[this.name])
    }

    //We don't care about the specific validation results if the property is valid.
    return valid ? true : result;
  }

  /**
   * Casts an value being set to the Type as defined by the property definition.
   * @param value the value to set this property to.
   * @param obj the parent object that this property is being set on
   * @returns {*}
   */
  cast (value, obj) {
    function subcast(val, obj, inst) {
{'fraction_non_alphanumeric': 0.10692268822637696, 'fraction_numerical': 0.0005053057099545225, 'mean_word_length': 2.2563343205001645, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, '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': '5965879', 'n_tokens_mistral': 2752, 'n_tokens_neox': 2682, 'n_words': 1124}