FeedEntrySelection.java

package net.sasasin.sreader.domain;

import java.util.Objects;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 * How to pick one entry from a feed. Each subtype holds only the values that selection kind needs,
 * so invalid combinations cannot be constructed.
 */
public sealed interface FeedEntrySelection
    permits FeedEntrySelection.First,
        FeedEntrySelection.Latest,
        FeedEntrySelection.ByIndex,
        FeedEntrySelection.ByTitleRegex,
        FeedEntrySelection.ByUrlRegex {

  record First() implements FeedEntrySelection {}

  record Latest() implements FeedEntrySelection {}

  record ByIndex(int index) implements FeedEntrySelection {
    public ByIndex {
      if (index < 0) {
        throw new IllegalArgumentException("entry index must be >= 0, but was: " + index);
      }
    }
  }

  record ByTitleRegex(String regex) implements FeedEntrySelection {
    public ByTitleRegex {
      regex = requireCompilableRegex(regex, "title regex");
    }
  }

  record ByUrlRegex(String regex) implements FeedEntrySelection {
    public ByUrlRegex {
      regex = requireCompilableRegex(regex, "url regex");
    }
  }

  static FeedEntrySelection first() {
    return new First();
  }

  static FeedEntrySelection latest() {
    return new Latest();
  }

  static FeedEntrySelection index(int idx) {
    return new ByIndex(idx);
  }

  static FeedEntrySelection titleRegex(String regex) {
    return new ByTitleRegex(regex);
  }

  static FeedEntrySelection urlRegex(String regex) {
    return new ByUrlRegex(regex);
  }

  private static String requireCompilableRegex(String regex, String label) {
    Objects.requireNonNull(regex, label + " must not be null");
    if (regex.isBlank()) {
      throw new IllegalArgumentException(label + " must not be blank");
    }
    try {
      Pattern.compile(regex);
    } catch (PatternSyntaxException e) {
      throw new IllegalArgumentException(label + " is not a valid Java regex: " + regex, e);
    }
    return regex;
  }
}