mirror of
https://github.com/RevenueCat/purchases-ios
synced 2026-11-09 22:35:56 +05:45
Splits a string into an array of fields, keeping empty ones, so rules can target locale subtags, product-id namespaces and similar composite values. A var path cannot index the result of an expression, so a positional field is read by wrapping the array in a one-element list and letting an iteration operator rebind scope to it. The array is also usable directly by in and by the iteration operators. Fields are never coerced to numbers, and a non-string operand or empty separator throws typeMismatch rather than splitting into characters. Co-authored-by: Cursor <cursoragent@cursor.com>
60 lines
2.3 KiB
Swift
60 lines
2.3 KiB
Swift
//
|
|
// SplitOperator.swift
|
|
//
|
|
// Created by Antonio Pallares.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
extension RulesEngine {
|
|
|
|
/// `rc.split` — splits a string into an array of fields.
|
|
enum SplitOperator {
|
|
|
|
/// `{"rc.split": [input, separator]}` — every field, including empty
|
|
/// ones, as strings. Numeric-looking fields are not coerced, so
|
|
/// `"1,2"` splits into `["1", "2"]`.
|
|
///
|
|
/// A `var` path cannot index the result of an expression, so a field is
|
|
/// read by wrapping the array in a one-element list and letting an
|
|
/// iteration operator rebind the scope to it:
|
|
///
|
|
/// ```json
|
|
/// {"some": [[{"rc.split": [{"var": "locale"}, "-"]}],
|
|
/// {"===": [{"var": "0"}, "es"]}]}
|
|
/// ```
|
|
///
|
|
/// The array is also usable directly by `in` and by the iteration
|
|
/// operators, which is enough for most membership and any/all tests.
|
|
///
|
|
/// Both operands must be strings and the separator must be non-empty,
|
|
/// otherwise `EvaluationError.typeMismatch`. An empty separator would
|
|
/// mean "split into characters", which the engine deliberately does not
|
|
/// offer; arity is exact for the same reason as `rc.semverCompare`.
|
|
static func opSplit(args: Value, vars: Scope) throws -> Value {
|
|
let evaluated = try Operators.evalArgs(args, vars: vars)
|
|
|
|
guard evaluated.count == 2 else {
|
|
throw EvaluationError.typeMismatch(
|
|
message: "operator 'rc.split' expects 2 arguments, got \(evaluated.count)"
|
|
)
|
|
}
|
|
|
|
guard case .string(let input) = evaluated[0] else {
|
|
throw EvaluationError.typeMismatch(
|
|
message: "operator 'rc.split' expected a string to split, got \(evaluated[0])"
|
|
)
|
|
}
|
|
|
|
guard case .string(let separator) = evaluated[1], !separator.isEmpty else {
|
|
throw EvaluationError.typeMismatch(
|
|
message: "operator 'rc.split' expected a non-empty string separator, "
|
|
+ "got \(evaluated[1])"
|
|
)
|
|
}
|
|
|
|
return .array(input.components(separatedBy: separator).map(Value.string))
|
|
}
|
|
}
|
|
}
|