001/*
002 * Copyright (C) 2022 - 2024, the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.github.ascopes.jct.compilers;
017
018/**
019 * Function representing a configuration operation that can be applied to a compiler.
020 *
021 * <p>This can allow encapsulating common configuration logic across tests into a single place.
022 *
023 * <p>Implementations of this interface should declare the exception type that the implementation
024 * can throw when invoked. If this is not a checked exception, or no case exists where an exception
025 * could be thrown, then this can be set to {@link RuntimeException}.
026 *
027 * <p>The following demonstrates an example usage of this interface. The implementation configures
028 * a specific annotation processor and sets an annotation processor flag.
029 *
030 * <pre><code>
031 * class MyAnnotationProcessorConfigurer implements JctCompilerConfigurer&lt;RuntimeException&gt; {
032 *    {@literal @Override}
033 *    public void configure(JctCompiler compiler) {
034 *      compiler
035 *          .addAnnotationProcessors(new MyAnnotationProcessor())
036 *          .addAnnotationProcessorOptions("MyAnnotationProcessor.debug=true");
037 *    }
038 * }
039 * </code></pre>
040 *
041 * <p>...tests can then make use of this configurer directly:
042 *
043 * <pre><code>
044 *    {@literal @Test}
045 *    void theCompilationSucceedsAsExpected() {
046 *      try (var workspace = Workspaces.newWorkspace()) {
047 *        // Given
048 *        ...
049 *
050 *        var compiler = JctCompilers
051 *            .newPlatformCompiler()
052 *            .release(17)
053 *            .configure(new MyAnnotationProcessorConfigurer());
054 *
055 *        // When
056 *        var compilation = compiler.compile(workspace);
057 *
058 *        // Then
059 *        ...
060 *      }
061 *    }
062 * </code></pre>
063 *
064 * <p>Since this is a functional interface, configurers can be lambda expressions, anonymous
065 * objects, or method references.
066 *
067 * <pre><code>
068 *   compiler
069 *      .configure(c -&gt; c.release(11))
070 *      .configure(this::configureFailures);
071 * </code></pre>
072 *
073 * <p>The JUnit support allows for specifying these configurers in an annotation instead. This
074 * will apply the configurer before passing it to the test as a parameter:
075 *
076 * <pre><code>
077 *   {@literal @JavacCompilersTest(configurers = {MyAnnotationProcessorConfigurer.class})}
078 *   void theCompilationSucceedsAsExpected(JctCompiler compiler) {
079 *     // ...
080 *   }
081 * </code></pre>
082 *
083 * <p>Note that in this case, the configurer must be an outer class or a static nested class
084 * rather than a Lambda expression, anonymous class, class instance, or nested class. It must also
085 * have a single public no-arguments constructor in order to be accessible.
086 *
087 * @param <E> the exception that may be thrown by the configurer.
088 * @author Ashley Scopes
089 * @since 0.0.1
090 */
091@FunctionalInterface
092public interface JctCompilerConfigurer<E extends Exception> {
093
094  /**
095   * Apply configuration logic to the given compiler.
096   *
097   * @param compiler the compiler.
098   * @throws E any exception that may be thrown by the configurer.
099   */
100  void configure(JctCompiler compiler) throws E;
101}