"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.nominal = exports.errors = exports.error = exports.all = exports.RefinedConstructorsTypeId = exports.BrandTypeId = void 0;
exports.refined = refined;
var Arr = _interopRequireWildcard(require("./Array.js"));
var Either = _interopRequireWildcard(require("./Either.js"));
var _Function = require("./Function.js");
var Option = _interopRequireWildcard(require("./Option.js"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
 * This module provides types and utility functions to create and work with branded types,
 * which are TypeScript types with an added type tag to prevent accidental usage of a value in the wrong context.
 *
 * The `refined` and `nominal` functions are both used to create branded types in TypeScript.
 * The main difference between them is that `refined` allows for validation of the data, while `nominal` does not.
 *
 * The `nominal` function is used to create a new branded type that has the same underlying type as the input, but with a different name.
 * This is useful when you want to distinguish between two values of the same type that have different meanings.
 * The `nominal` function does not perform any validation of the input data.
 *
 * On the other hand, the `refined` function is used to create a new branded type that has the same underlying type as the input,
 * but with a different name, and it also allows for validation of the input data.
 * The `refined` function takes a predicate that is used to validate the input data.
 * If the input data fails the validation, a `BrandErrors` is returned, which provides information about the specific validation failure.
 *
 * @since 2.0.0
 */

/**
 * @since 2.0.0
 * @category symbols
 */
const BrandTypeId = exports.BrandTypeId = /*#__PURE__*/Symbol.for("effect/Brand");
/**
 * @since 2.0.0
 * @category symbols
 */
const RefinedConstructorsTypeId = exports.RefinedConstructorsTypeId = /*#__PURE__*/Symbol.for("effect/Brand/Refined");
/**
 * Returns a `BrandErrors` that contains a single `RefinementError`.
 *
 * @since 2.0.0
 * @category constructors
 */
const error = (message, meta) => [{
  message,
  meta
}];
/**
 * Takes a variable number of `BrandErrors` and returns a single `BrandErrors` that contains all refinement errors.
 *
 * @since 2.0.0
 * @category constructors
 */
exports.error = error;
const errors = (...errors) => Arr.flatten(errors);
exports.errors = errors;
function refined(...args) {
  const either = args.length === 2 ? unbranded => args[0](unbranded) ? Either.right(unbranded) : Either.left(args[1](unbranded)) : unbranded => {
    return Option.match(args[0](unbranded), {
      onNone: () => Either.right(unbranded),
      onSome: Either.left
    });
  };
  return Object.assign(unbranded => Either.getOrThrowWith(either(unbranded), _Function.identity), {
    [RefinedConstructorsTypeId]: RefinedConstructorsTypeId,
    option: args => Option.getRight(either(args)),
    either,
    is: args => Either.isRight(either(args))
  });
}
/**
 * This function returns a `Brand.Constructor` that **does not apply any runtime checks**, it just returns the provided value.
 * It can be used to create nominal types that allow distinguishing between two values of the same type but with different meanings.
 *
 * If you also want to perform some validation, see {@link refined}.
 *
 * @example
 * ```ts
 * import * as assert from "node:assert"
 * import { Brand } from "effect"
 *
 * type UserId = number & Brand.Brand<"UserId">
 *
 * const UserId = Brand.nominal<UserId>()
 *
 * assert.strictEqual(UserId(1), 1)
 * ```
 *
 * @since 2.0.0
 * @category constructors
 */
const nominal = () => {
  // @ts-expect-error
  return Object.assign(args => args, {
    [RefinedConstructorsTypeId]: RefinedConstructorsTypeId,
    option: args => Option.some(args),
    either: args => Either.right(args),
    is: _args => true
  });
};
/**
 * Combines two or more brands together to form a single branded type.
 * This API is useful when you want to validate that the input data passes multiple brand validators.
 *
 * @example
 * ```ts
 * import * as assert from "node:assert"
 * import { Brand } from "effect"
 *
 * type Int = number & Brand.Brand<"Int">
 * const Int = Brand.refined<Int>(
 *   (n) => Number.isInteger(n),
 *   (n) => Brand.error(`Expected ${n} to be an integer`)
 * )
 * type Positive = number & Brand.Brand<"Positive">
 * const Positive = Brand.refined<Positive>(
 *   (n) => n > 0,
 *   (n) => Brand.error(`Expected ${n} to be positive`)
 * )
 *
 * const PositiveInt = Brand.all(Int, Positive)
 *
 * assert.strictEqual(PositiveInt(1), 1)
 * assert.throws(() => PositiveInt(1.1))
 * ```
 *
 * @since 2.0.0
 * @category combining
 */
exports.nominal = nominal;
const all = (...brands) => {
  const either = args => {
    let result = Either.right(args);
    for (const brand of brands) {
      const nextResult = brand.either(args);
      if (Either.isLeft(result) && Either.isLeft(nextResult)) {
        result = Either.left([...result.left, ...nextResult.left]);
      } else {
        result = Either.isLeft(result) ? result : nextResult;
      }
    }
    return result;
  };
  // @ts-expect-error
  return Object.assign(args => Either.match(either(args), {
    onLeft: e => {
      throw e;
    },
    onRight: _Function.identity
  }), {
    [RefinedConstructorsTypeId]: RefinedConstructorsTypeId,
    option: args => Option.getRight(either(args)),
    either,
    is: args => Either.isRight(either(args))
  });
};
exports.all = all;
//# sourceMappingURL=Brand.js.map