Newer
Older
vavr-effect / src / main / java / com / github / tonivade / vavr / effect / IOConnection.java
/*
 * Copyright (c) 2024-2025, Antonio Gabriel Muñoz Conejo <me at tonivade dot es>
 * Distributed under the terms of the MIT License
 */
package com.github.tonivade.vavr.effect;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.UnaryOperator;

sealed interface IOConnection {

  IOConnection UNCANCELLABLE = new Uncancellable();

  boolean isCancellable();

  void setCancelToken(IO<Unit> cancel);

  void cancelNow();

  void cancel();

  StateIO updateState(UnaryOperator<StateIO> update);

  static IOConnection cancellable() {
    return new Cancellable();
  }

  static final class Uncancellable implements IOConnection {

    private Uncancellable() { }

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

    @Override
    public void setCancelToken(IO<Unit> cancel) {
      // uncancellable
    }

    @Override
    public void cancelNow() {
      // uncancellable
    }

    @Override
    public void cancel() {
      // uncancellable
    }

    @Override
    public StateIO updateState(UnaryOperator<StateIO> update) {
      return StateIO.INITIAL;
    }
  }

  static final class Cancellable implements IOConnection {

    private IO<Unit> cancelToken = IO.UNIT;
    private final AtomicReference<StateIO> state = new AtomicReference<>(StateIO.INITIAL);

    private Cancellable() { }

    @Override
    public boolean isCancellable() {
      return true;
    }

    @Override
    public void setCancelToken(IO<Unit> cancel) {
      this.cancelToken = Objects.requireNonNull(cancel);
    }

    @Override
    public void cancelNow() {
      cancelToken.runAsync();
    }

    @Override
    public void cancel() {
      if (state.getAndUpdate(StateIO::cancellingNow).isCancelable()) {
        cancelNow();

        state.set(StateIO.CANCELLED);
      }
    }

    @Override
    public StateIO updateState(UnaryOperator<StateIO> update) {
      return state.updateAndGet(update::apply);
    }
  }
}