SingleLensCore.java

package sprouts.impl;

import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import sprouts.Channel;
import sprouts.Lens;
import sprouts.Val;
import sprouts.Var;

import java.util.Collections;
import java.util.List;

/**
 * A {@link LensCore} backed by a single parent {@link Var} and a {@link Lens}.
 * Fetches the current item by applying the lens getter to the parent,
 * and writes back by applying the lens wither.
 *
 * @param <A> The item type of the parent property.
 * @param <T> The item type of this lens property.
 */
final class SingleLensCore<A extends @Nullable Object, T extends @Nullable Object> implements LensCore<T> {

    private static final Logger log = org.slf4j.LoggerFactory.getLogger(SingleLensCore.class);

    private final Var<A>                       _parent;
    private final Lens<@Nullable A, @Nullable T> _lens;

    SingleLensCore(Var<A> parent, Lens<@Nullable A, @Nullable T> lens) {
        _parent = parent;
        _lens   = lens;
    }

    @Override
    public @Nullable T fetchFromSources(@Nullable T lastKnownItem) {
        T fetchedValue = lastKnownItem;
        try {
            fetchedValue = _lens.getter(Util.fakeNonNull(_parent.orElseNull()));
        } catch ( Exception e ) {
            Util.sneakyThrowExceptionIfFatal(e);
            String parentId = _parent.id().isEmpty() ? "?" : "'" + _parent.id() + "'";
            Util._logError(log,
                    "Failed to fetch item for property lens from parent " +
                    "property {} (with item type '{}') using the current lens getter.",
                    parentId, _parent.type(), e
            );
        }
        return fetchedValue;
    }

    @Override
    public void writeToSources(Channel channel, @Nullable T newItem) {
        try {
            A newParentItem = _lens.wither(Util.fakeNonNull(_parent.orElseNull()), Util.fakeNonNull(newItem));
            _parent.set(channel, newParentItem);
        } catch ( Exception e ) {
            Util.sneakyThrowExceptionIfFatal(e);
            String parentId = _parent.id().isEmpty() ? "?" : "'" + _parent.id() + "'";
            Util._logError(log,
                    "Property lens failed to update its parent " +
                    "property {} (with item type '{}') using the current setter lambda!",
                    parentId, _parent.type(), e
            );
        }
    }

    @Override
    public List<? extends Val<?>> sources() {
        return Collections.singletonList(_parent);
    }

    @Override
    public boolean shouldSuppressSourceCallback() {
        return false;
    }

    @Override
    public String coreName() {
        return "Lens";
    }

    @Override
    public LensCore<T> newInstance() {
        return this; // no mutable state — safe to share
    }
}