class SwapCastsInLikeOperators implements Rule<Function> {

    private final Set<String> LIKE_OPERATORS = Set.of(LikeOperators.OP_LIKE, LikeOperators.OP_ILIKE);

    private final Capture<Function> castCapture;
    private final Pattern<Function> pattern;

    public SwapCastsInLikeOperators(FunctionSymbolResolver functionResolver) {
        this.castCapture = new Capture<>();
        this.pattern = typeOf(Function.class)
            .with(f -> LIKE_OPERATORS.contains(f.name()))
            .with(f -> f.arguments().get(1).symbolType().isValueOrParameterSymbol())
            .with(f -> Optional.of(f.arguments().get(0)), typeOf(Function.class).capturedAs(castCapture)
                .with(f -> f.isCast())
                .with(f -> f.arguments().get(0) instanceof Reference ref && ref.valueType().id() == StringType.ID)
            );
    }

    @Override
    public Pattern<Function> pattern() {
        return pattern;
    }

    @Override
    public Symbol apply(Function likeFunction, Captures captures, NodeContext nodeCtx, Symbol parentNode) {<FILL_FUNCTION_BODY>}
}

        var literalOrParam = likeFunction.arguments().get(1);
        var castFunction = captures.get(castCapture);
        var reference = castFunction.arguments().get(0);
        CastMode castMode = castFunction.castMode();
        assert castMode != null : "Pattern matched, function must be a cast";
        Symbol castedLiteral = literalOrParam.cast(StringType.INSTANCE, castMode);
        List<Symbol> newArgs;
        if (likeFunction.arguments().size() == 3) {
            // Don't lose ESCAPE character.
            newArgs = List.of(reference, castedLiteral, likeFunction.arguments().get(2));
        } else {
            newArgs = List.of(reference, castedLiteral);
        }
        return new Function(
            likeFunction.signature(),
            newArgs,
            likeFunction.valueType()
        );
    
<no_super_class>