CopyDocsToSiteMojo.java

package network.ike.docs.plugin;

import org.apache.maven.api.plugin.MojoException;
import org.apache.maven.api.plugin.annotations.Mojo;
import org.apache.maven.api.plugin.annotations.Parameter;

import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;

/**
 * Copy rendered HTML docs and assets into the Maven site output directory.
 *
 * <p>Copies HTML files plus supporting assets (SVG diagrams, PNG/JPG
 * images, CSS stylesheets) from the Asciidoctor generated-docs directory
 * into {@code target/site/docs/} so they are deployed alongside the
 * Maven site.
 *
 * <p>Replaces: {@code copy-docs-to-site.sh}
 *
 * <p>Usage:
 * <pre>
 * mvn idoc:copy-docs -DgeneratedDocsDir=target/generated-docs/html -DsiteDir=target/site
 * </pre>
 */
@Mojo(name = "copy-docs",
      defaultPhase = "site")
public class CopyDocsToSiteMojo implements org.apache.maven.api.plugin.Mojo {

    @org.apache.maven.api.di.Inject
    private org.apache.maven.api.plugin.Log log;
    /**
     * Access the Maven logger.
     *
     * @return the logger
     */
    protected org.apache.maven.api.plugin.Log getLog() { return log; }

    /** Directory containing rendered HTML and assets. */
    @Parameter(property = "generatedDocsDir", required = true)
    File generatedDocsDir;

    /** Maven site output directory (e.g., {@code target/site}). */
    @Parameter(property = "siteDir", required = true)
    File siteDir;

    /** File extensions to copy from generated-docs to the site. */
    public static final List<String> COPY_EXTENSIONS =
            List.of(".html", ".svg", ".png", ".jpg", ".css");

    /** Creates this goal instance. */
    public CopyDocsToSiteMojo() {}

    @Override
    public void execute() throws MojoException {
        if (!generatedDocsDir.isDirectory()) {
            getLog().info("copy-docs: generated-docs directory does not exist, "
                    + "skipping — " + generatedDocsDir);
            return;
        }

        Path source = generatedDocsDir.toPath();
        Path dest = siteDir.toPath().resolve("docs");

        int copied = 0;
        try {
            Files.createDirectories(dest);

            for (String ext : COPY_EXTENSIONS) {
                copied += copyByExtension(source, dest, ext);
            }
        } catch (IOException e) {
            throw new MojoException(
                    "Failed to copy docs to site", e);
        }

        if (copied > 0) {
            getLog().info("copy-docs: copied " + copied
                    + " file(s) to " + dest);
        } else {
            getLog().info("copy-docs: no matching files in "
                    + generatedDocsDir);
        }
    }

    /**
     * Copy all files with the given extension from source to dest.
     *
     * @return number of files copied
     */
    private int copyByExtension(Path source, Path dest, String extension)
            throws IOException {
        int count = 0;
        try (DirectoryStream<Path> stream =
                     Files.newDirectoryStream(source, "*" + extension)) {
            for (Path file : stream) {
                if (Files.isRegularFile(file)) {
                    Files.copy(file, dest.resolve(file.getFileName()),
                            StandardCopyOption.REPLACE_EXISTING);
                    count++;
                }
            }
        }
        return count;
    }

    // ── Pure testable functions ──────────────────────────────────────

    /**
     * Check whether a filename has an extension in the copy list.
     *
     * @param filename the filename to check
     * @return true if the file should be copied
     */
    public static boolean shouldCopy(String filename) {
        String lower = filename.toLowerCase();
        return COPY_EXTENSIONS.stream().anyMatch(lower::endsWith);
    }
}