SemVer.java

package space.sunqian.fs.utils.version;

import space.sunqian.annotation.Immutable;
import space.sunqian.annotation.Nonnull;
import space.sunqian.annotation.Nullable;

import java.util.List;

/**
 * This interface represents the semantic version as defined by <a href="https://semver.org/">Semantic Versioning</a>.
 * <p>
 * A semantic version has the format {@code MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]} where:
 * <ul>
 *     <li>
 *         {@code MAJOR}, {@code MINOR}, and {@code PATCH} are non-negative integers.
 *     </li>
 *     <li>
 *         {@code PRERELEASE} is a dot-separated list of identifiers consisting of alphanumeric characters and
 *         hyphens ([0-9A-Za-z-]).
 *     </li>
 *     <li>
 *         {@code BUILD} is a dot-separated list of identifiers consisting of alphanumeric characters and hyphens
 *         ([0-9A-Za-z-]).
 *     </li>
 * </ul>
 *
 * @author sunqian
 */
@Immutable
public interface SemVer extends Comparable<SemVer> {

    /**
     * Parses the given string as a semantic version.
     *
     * @param version the string to parse
     * @return the parsed semantic version
     * @throws IllegalArgumentException if the given string is not a valid semantic version
     */
    static @Nonnull SemVer parse(@Nonnull String version) throws IllegalArgumentException {
        return new SemVerImpl(version);
    }

    /**
     * Creates a semantic version with the given major, minor, and patch version numbers.
     *
     * @param major the major version number
     * @param minor the minor version number
     * @param patch the patch version number
     * @return the created semantic version
     */
    static @Nonnull SemVer of(int major, int minor, int patch) {
        return of(major, minor, patch, null, null);
    }

    /**
     * Creates a semantic version with the given major, minor, patch version numbers, pre-release version, and build
     * metadata.
     *
     * @param major      the major version number
     * @param minor      the minor version number
     * @param patch      the patch version number
     * @param preRelease the pre-release version
     * @param buildMeta  the build metadata
     * @return the created semantic version
     * @throws IllegalArgumentException if the pre-release version or build metadata is invalid
     */
    static @Nonnull SemVer of(
        int major, int minor, int patch, @Nullable String preRelease, @Nullable String buildMeta
    ) throws IllegalArgumentException {
        return parse(
            major + "." + minor + "." + patch
                + (preRelease == null ? "" : "-" + preRelease)
                + (buildMeta == null ? "" : "+" + buildMeta)
        );
    }

    /**
     * Returns the major version number.
     *
     * @return the major version number
     */
    int major();

    /**
     * Returns the minor version number.
     *
     * @return the minor version number
     */
    int minor();

    /**
     * Returns the patch version number.
     *
     * @return the patch version number
     */
    int patch();

    /**
     * Returns the pre-release version in the current {@link SemVer}.
     *
     * @return the pre-release version in the current {@link SemVer}
     */
    @Nullable
    PreRelease preRelease();

    /**
     * Returns the build metadata in the current {@link SemVer}.
     *
     * @return the build metadata in the current {@link SemVer}
     */
    @Nullable
    BuildMeta buildMeta();

    /**
     * Returns this {@link SemVer} as a string.
     *
     * @return this {@link SemVer} as a string
     */
    @Nonnull
    String toString();

    /**
     * Returns whether the current {@link SemVer} is equal to the given object.
     *
     * @param other the object to compare with
     * @return whether the current {@link SemVer} is equal to the given object
     */
    boolean equals(@Nullable Object other);

    /**
     * Represents the pre-release version in the {@link SemVer}.
     */
    interface PreRelease extends Comparable<PreRelease> {

        /**
         * Returns the pre-release version identifiers in the {@link SemVer}. The type of each identifier is either
         * {@link String} or {@link Integer}.
         *
         * @return the pre-release version identifiers in the {@link SemVer}
         */
        @Nonnull
        List<@Nonnull Object> identifiers();

        /**
         * Returns the pre-release version in the {@link SemVer} as a string.
         *
         * @return the pre-release version in the {@link SemVer} as a string
         */
        @Nonnull
        String toString();

        /**
         * Returns whether the current {@link PreRelease} is equal to the given object.
         * <p>
         * Note that this method only compares its own content, regardless of the instance of {@link SemVer} it belongs
         * to.
         *
         * @param other the object to compare with
         * @return whether the current {@link PreRelease} is equal to the given object
         */
        boolean equals(@Nullable Object other);

        /**
         * Compares this {@link PreRelease} with the specified {@link PreRelease} for order.
         * <p>
         * Returns a negative integer, zero, or a positive integer as this {@link PreRelease} is less than, equal to, or
         * greater than the specified {@link PreRelease}.
         * <p>
         * Note that this method only compares its own content, regardless of the instance of {@link SemVer} it belongs
         * to.
         *
         * @param o the {@link PreRelease} to be compared
         * @return a negative integer, zero, or a positive integer as this {@link PreRelease} is less than, equal to, or
         * greater than the specified {@link PreRelease}
         */
        @Override
        int compareTo(@Nonnull PreRelease o);
    }

    /**
     * Represents the build metadata in the {@link SemVer}.
     */
    interface BuildMeta {

        /**
         * Returns the build metadata identifiers in the {@link SemVer}.
         *
         * @return the build metadata identifiers in the {@link SemVer}
         */
        @Nonnull
        List<@Nonnull String> identifiers();

        /**
         * Returns the build metadata in the {@link SemVer} as a string.
         *
         * @return the build metadata in the {@link SemVer} as a string
         */
        @Nonnull
        String toString();

        /**
         * Returns whether the current {@link BuildMeta} is equal to the given object.
         * <p>
         * Note that this method only compares its own content, regardless of the instance of {@link SemVer} it belongs
         * to.
         *
         * @param other the object to compare with
         * @return whether the current {@link BuildMeta} is equal to the given object
         */
        boolean equals(@Nullable Object other);
    }
}