content: /*
   * DazzleConf
   * Copyright © 2025 Anand Beh
   *
   * DazzleConf is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * DazzleConf is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   * GNU Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License
   * along with DazzleConf. If not, see <https://www.gnu.org/licenses/>
   * and navigate to version 3 of the GNU Lesser General Public License.
   */
  
  package space.arim.dazzleconf.backend;
  
  import org.checkerframework.checker.nullness.qual.NonNull;
  
  import java.io.IOException;
  import java.util.Objects;
  
  /**
   * A displayable item, printed in one of multiple ways.
   * <p>
   * This interface exposes multiple printing methods, each named with "print." No matter the printing method used, the
   * content generated by each method must be the same result. For example, the following assertion should pass:
   * <pre>
   *     {@code
   *     Printable printable = ...;
   *     StringBuilder builder = new StringBuilder();
   *     printable.printTo(builder);
   *     assert printable.printString().equals(builder.toString());
   *     }
   * </pre>
   * <b>Purpose</b>
   * <p>
   * A {@code Printable} is typically passed to a method which expects to display information somewhere. The purpose is
   * to provide late resolution and type-based concatenation of error messages, as opposed to eager construction of
   * strings. Many error messages can be quite long, and might be better streamed.
   * <p>
   * <b>Mutability</b>
   * <p>
   * Mutability is left intentionally undefined. Instances of this type are not usually stored, but passed and consumed,
   * which means that consumers will (usually) need to call one of the "print" methods at most once.
   */
  public interface Printable {
  
      /**
       * Gets the printed content as a string
       *
       * @return printed string
       */
      @NonNull String printString();
  
      /**
       * Prints to the given appendable
       *
       * @param output where to place the message
       * @throws IOException if the output threw this error, it is propagated
       */
      void printTo(@NonNull Appendable output) throws IOException;
  
      /**
       * Prints to the given builder
       *
       * @param output where to place the message
       */
      void printTo(@NonNull StringBuilder output);
  
      /**
       * Gets a string representation.
       * <p>
       * Implementations are strongly encouraged (but not required) to return the same value as fo {@link #printString()}
       *
       * @return a string representation of this object
       */
      @Override
      @NonNull String toString();
  
      /**
       * Makes a simple implementation based on an existing string. The argument will be yielded by each of the printing
       * methods.
       *
       * @param content the prebuilt string or character sequence
       * @return a printable using it
       */
      static @NonNull Printable preBuilt(@NonNull CharSequence content) {
          Objects.requireNonNull(content);
          return new Printable() {
              @Override
              public @NonNull String printString() {
                  return content.toString();
              }
  
              @Override
              public void printTo(@NonNull Appendable output) throws IOException {
                  output.append(content);
              }
  
              @Override
              public void printTo(@NonNull StringBuilder output) {
                  output.append(content);
              }
  
              @Override
              public @NonNull String toString() {
                  return content.toString();
              }
          };
      }
  
      /**
       * Joins multiple printable values, appending them one after the other.
       * <p>
       * This method creates a new {@code Printable} which combines the arguments in the sequence they are provided.
       * If the returned result is printed, it will be identically to printing each of the arguments in a loop.
       * <p>
       * If the caller modifies the {@code values} array after calling this function, it is not defined whether such
       * modification will affect or not affect the returned {@code Printable}.
       *
       * @param values the values to join together
       * @return a printable which prints each of the arguments, in sequence
       */
      static @NonNull Printable join(@NonNull Printable @NonNull ...values) {
          Objects.requireNonNull(values);
          return new Printable.Abstract() {
              @Override
              public void printTo(@NonNull Appendable output) throws IOException {
                  for (Printable value : values) {
                      value.printTo(output);
                  }
              }
          };
      }
  
      /**
       * A base implementation for {@code Printable}.
       * <p>
       * Usage is simple. Extend this class and implement the only required method, {@link #printTo(Appendable)}.
       * This class implements the other "print" methods, as well as {@code toString}.
       *
       */
      abstract class Abstract implements Printable {
  
          /**
           * Creates
           */
          public Abstract() {}
  
          @Override
          public final @NonNull String printString() {
              StringBuilder builder = new StringBuilder();
              printTo(builder);
              return builder.toString();
          }
  
          @Override
          public final void printTo(@NonNull StringBuilder output) {
              try {
                  printTo((Appendable) output);
              } catch (IOException ex) {
                  throw new AssertionError("StringBuilder does not throw IOException", ex);
              }
          }
  
          @Override
          public final @NonNull String toString() {
              return printString();
          }
      }
  }
  