mirror of
https://github.com/RevenueCat/purchases-ios
synced 2026-11-11 22:55:55 +05:45
100 lines
3.4 KiB
Swift
100 lines
3.4 KiB
Swift
//
|
|
// Operators.swift
|
|
//
|
|
// Created by Antonio Pallares.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// JSON Logic operator dispatcher and shared helpers.
|
|
///
|
|
/// Operators are responsible for evaluating their own arguments. Most use
|
|
/// the `evalTwo` / `evalArgs` helpers which evaluate eagerly; short-circuit
|
|
/// operators (`and`, `or`, `if`) iterate manually.
|
|
enum Operators {
|
|
|
|
/// Dispatch a JSON Logic operator. Throws `RuleError.unsupportedOperator`
|
|
/// when the operator name isn't implemented in this slice.
|
|
// swiftlint:disable:next cyclomatic_complexity
|
|
static func dispatch(
|
|
op operatorName: String,
|
|
args: Value,
|
|
vars: Value
|
|
) throws -> Value {
|
|
switch operatorName {
|
|
case "var":
|
|
return try AccessorOperators.opVar(args: args, vars: vars)
|
|
case "missing":
|
|
return try AccessorOperators.opMissing(args: args, vars: vars)
|
|
|
|
case "==":
|
|
return try EqualityOperators.opLooseEq(args: args, vars: vars)
|
|
case "!=":
|
|
return try EqualityOperators.opLooseNe(args: args, vars: vars)
|
|
case "===":
|
|
return try EqualityOperators.opStrictEq(args: args, vars: vars)
|
|
case "!==":
|
|
return try EqualityOperators.opStrictNe(args: args, vars: vars)
|
|
|
|
case "!":
|
|
return try LogicOperators.opNot(args: args, vars: vars)
|
|
case "!!":
|
|
return try LogicOperators.opNotNot(args: args, vars: vars)
|
|
case "and":
|
|
return try LogicOperators.opAnd(args: args, vars: vars)
|
|
case "or":
|
|
return try LogicOperators.opOr(args: args, vars: vars)
|
|
case "if":
|
|
return try LogicOperators.opIf(args: args, vars: vars)
|
|
|
|
case "+":
|
|
return try ArithmeticOperators.opAdd(args: args, vars: vars)
|
|
case "-":
|
|
return try ArithmeticOperators.opSub(args: args, vars: vars)
|
|
case "*":
|
|
return try ArithmeticOperators.opMul(args: args, vars: vars)
|
|
case "/":
|
|
return try ArithmeticOperators.opDiv(args: args, vars: vars)
|
|
case "%":
|
|
return try ArithmeticOperators.opMod(args: args, vars: vars)
|
|
|
|
default:
|
|
throw RuleError.unsupportedOperator(name: operatorName)
|
|
}
|
|
}
|
|
|
|
// MARK: - Shared helpers
|
|
|
|
/// Treat an operator argument as an argument list. Per JSON Logic, a
|
|
/// single-value argument is implicitly wrapped in a one-element list,
|
|
/// so `{"!": true}` and `{"!": [true]}` are equivalent.
|
|
static func argsAsList(_ args: Value) -> [Value] {
|
|
if case .array(let items) = args {
|
|
return items
|
|
}
|
|
return [args]
|
|
}
|
|
|
|
/// Evaluate every element in an argument list.
|
|
static func evalArgs(_ args: Value, vars: Value) throws -> [Value] {
|
|
try argsAsList(args).map { try Evaluator.evaluateValue($0, vars: vars) }
|
|
}
|
|
|
|
/// Evaluate exactly two arguments. Used by binary operators (`==`, `!=`,
|
|
/// `===`, `!==`, and the comparison operators a future iteration will
|
|
/// add).
|
|
static func evalTwo(
|
|
_ args: Value,
|
|
vars: Value,
|
|
opName: String
|
|
) throws -> (Value, Value) {
|
|
let evaluated = try evalArgs(args, vars: vars)
|
|
guard evaluated.count == 2 else {
|
|
throw RuleError.typeMismatch(
|
|
message: "operator '\(opName)' expects 2 arguments, got \(evaluated.count)"
|
|
)
|
|
}
|
|
return (evaluated[0], evaluated[1])
|
|
}
|
|
}
|