FeedImportPlanner.java

package net.sasasin.sreader.service.feed.toml;

import java.util.Objects;
import net.sasasin.sreader.domain.FeedStatus;
import net.sasasin.sreader.domain.FeedUrl;

/** Pure state-transition policy for a single imported feed. */
final class FeedImportPlanner {
  FeedImportDecision plan(
      FeedUrl current, FeedTomlService.ImportFeed desired, boolean resubscribe) {
    if (current == null) {
      return new FeedImportDecision.Insert(desired);
    }
    boolean methodChanged = current.fullTextMethod() != desired.fullTextMethod();
    if (current.status() == FeedStatus.ACTIVE && desired.status() == FeedStatus.ACTIVE) {
      return methodChanged
          ? new FeedImportDecision.UpdateMethod(desired)
          : new FeedImportDecision.Unchanged();
    }
    if (current.status() == FeedStatus.ACTIVE) {
      return new FeedImportDecision.Unsubscribe(desired);
    }
    if (desired.status() == FeedStatus.UNSUBSCRIBED) {
      boolean metadataChanged =
          !Objects.equals(current.unsubscribeReason(), desired.unsubscribeReason())
              || !Objects.equals(current.unsubscribedAt(), desired.unsubscribedAt())
              || !Objects.equals(current.note(), desired.note());
      return metadataChanged || methodChanged
          ? new FeedImportDecision.UpdateUnsubscribedMetadata(desired, methodChanged)
          : new FeedImportDecision.Unchanged();
    }
    return resubscribe
        ? new FeedImportDecision.Resubscribe(desired)
        : new FeedImportDecision.Conflict(desired);
  }

  sealed interface FeedImportDecision
      permits FeedImportDecision.Insert,
          FeedImportDecision.UpdateMethod,
          FeedImportDecision.Unsubscribe,
          FeedImportDecision.UpdateUnsubscribedMetadata,
          FeedImportDecision.Resubscribe,
          FeedImportDecision.Unchanged,
          FeedImportDecision.Conflict {
    record Insert(FeedTomlService.ImportFeed feed) implements FeedImportDecision {}

    record UpdateMethod(FeedTomlService.ImportFeed feed) implements FeedImportDecision {}

    record Unsubscribe(FeedTomlService.ImportFeed feed) implements FeedImportDecision {}

    record UpdateUnsubscribedMetadata(FeedTomlService.ImportFeed feed, boolean methodChanged)
        implements FeedImportDecision {}

    record Resubscribe(FeedTomlService.ImportFeed feed) implements FeedImportDecision {}

    record Unchanged() implements FeedImportDecision {}

    record Conflict(FeedTomlService.ImportFeed feed) implements FeedImportDecision {}
  }
}