Files
Antonio PallaresandCursor 863a4b1f26 Add rc.regexReplace custom operator (#7558)
* Add rc.regexReplace custom operator

Co-authored-by: Cursor <cursoragent@cursor.com>

* Place the replacement template divergence on the right axis

The note blamed the three regex engines, but the template is parsed by
each platform's own API and never reaches ICU, so this is the one part of
the feature where the two devices differ rather than agreeing against
Funnels. Records what each actually does with $& and with a $1 the
pattern cannot supply.

Co-authored-by: Cursor <cursoragent@cursor.com>

* State the replacement contract without surveying the engines

What a reader needs is that the replacement is taken literally; which
template syntax each engine would otherwise have honoured is background
that belongs with whoever authors the patterns.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Compose rc.regexReplace with an operator that still exists

The fixture proving a replaced string is usable downstream fed it to
rc.length, which main has since removed. rc.upper keeps the point of the
case without depending on an operator that is gone.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 13:09:59 +02:00

65 lines
2.1 KiB
Swift

//
// CustomOperators.swift
//
// Created by Antonio Pallares.
//
import Foundation
extension RulesEngine {
/// RevenueCat-specific JSON Logic operators.
///
/// Operators here are namespaced under `rc.` so they cannot collide with a
/// future standard JSON Logic operator.
enum CustomOperators {
// swiftlint:disable:next cyclomatic_complexity
static func dispatch(
op operatorName: String,
args: Value,
vars: Scope
) throws -> Value {
switch operatorName {
case "rc.entries":
return try EntriesOperators.opEntries(args: args, vars: vars)
case "rc.fromEntries":
return try EntriesOperators.opFromEntries(args: args, vars: vars)
case "rc.let":
return try LetOperator.opLet(args: args, vars: vars)
case "rc.lower":
return try CaseOperators.opLower(args: args, vars: vars)
case "rc.upper":
return try CaseOperators.opUpper(args: args, vars: vars)
case "rc.regexExtract":
return try RegexOperators.opRegexExtract(args: args, vars: vars)
case "rc.regexMatch":
return try RegexOperators.opRegexMatch(args: args, vars: vars)
case "rc.regexReplace":
return try RegexOperators.opRegexReplace(args: args, vars: vars)
case "rc.rootVar":
return try RootVarOperator.opRootVar(args: args, vars: vars)
case "rc.semverCompare":
return try SemverOperator.opSemverCompare(args: args, vars: vars)
case "rc.slice":
return try SliceOperator.opSlice(args: args, vars: vars)
case "rc.sortBy":
return try SortByOperator.opSortBy(args: args, vars: vars)
case "rc.split":
return try SplitOperator.opSplit(args: args, vars: vars)
default:
throw EvaluationError.unsupportedOperator(name: operatorName)
}
}
}
}