001/*
002 * JDrupes GitVersioning
003 * Copyright (C) 2025 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.gitversioning.api;
020
021import java.nio.file.Path;
022import java.util.Objects;
023import java.util.ServiceLoader;
024import java.util.ServiceLoader.Provider;
025import java.util.stream.Stream;
026import org.eclipse.jgit.lib.Repository;
027
028/**
029 * Configurable version evaluator for a Git repository.
030 *
031 * <p>By default, all files in the work tree are considered when evaluating
032 * the version. Restrict the scope using the {@code matching*} methods or
033 * {@link #subDirectory(java.nio.file.Path)}. Multiple matchers are combined
034 * with a logical OR.
035 *
036 * <p>Obtain an instance via {@link #forRepository(Repository)}
037 * or {@link #forRepository(Repository, ClassLoader)}.
038 */
039public interface VersionEvaluator {
040
041    /**
042     * Creates a version evaluator for the given repository. The
043     * implementation is looked up using the {@link ServiceLoader} mechanism
044     * with the given class loader.
045     *
046     * @param repository the repository
047     * @param classLoader the class loader to use for service loading
048     * @return the version evaluator
049     * @throws java.util.NoSuchElementException if no
050     * {@link VersionEvaluatorProvider} is available
051     */
052    static VersionEvaluator forRepository(Repository repository,
053            ClassLoader classLoader) {
054        ServiceLoader<VersionEvaluatorProvider> loader
055            = ServiceLoader.load(VersionEvaluatorProvider.class, classLoader);
056        return loader.findFirst().orElseThrow().repository(repository);
057    }
058
059    /**
060     * Creates a version evaluator for the given repository. The
061     * implementation is looked up using the {@link ServiceLoader} mechanism,
062     * trying the context class loader of the current thread and the class
063     * loader of this class.
064     *
065     * @param repository the repository
066     * @return the version evaluator
067     * @throws java.util.NoSuchElementException if no
068     * {@link VersionEvaluatorProvider} is available
069     */
070    static VersionEvaluator forRepository(Repository repository) {
071        return Stream.of(Thread.currentThread().getContextClassLoader(),
072            VersionEvaluator.class.getClassLoader()).filter(Objects::nonNull)
073            .map(cl -> ServiceLoader.load(VersionEvaluatorProvider.class, cl)
074                .stream())
075            .flatMap(s -> s).findFirst().map(Provider::get).orElseThrow()
076            .repository(repository);
077    }
078
079    /**
080     * Returns the evaluator's repository.
081     *
082     * @return the repository
083     */
084    Repository repository();
085
086    /**
087     * Sets the tag filter to use. The filter determines which tags are
088     * recognized as version tags and extracts the version string from them.
089     *
090     * @param tagFilter the tag filter
091     * @return this evaluator for chaining
092     */
093    VersionEvaluator tagFilter(TagFilter tagFilter);
094
095    /**
096     * Sets the tag processor to use. The processor generates the final
097     * version string from the tag name and parsed version.
098     *
099     * @param tagProcessor the tag processor
100     * @return this evaluator for chaining
101     */
102    VersionEvaluator tagProcessor(TagProcessor tagProcessor);
103
104    /**
105     * Include all files matching the given glob expression when evaluating
106     * the version.
107     *
108     * @param glob the glob expression
109     * @return this evaluator for chaining
110     */
111    VersionEvaluator matchingGlob(String glob);
112
113    /**
114     * Include all files matching the given regular expression when evaluating
115     * the version.
116     *
117     * @param regex the regular expression
118     * @return this evaluator for chaining
119     */
120    VersionEvaluator matchingRegex(String regex);
121
122    /**
123     * Include all files matching the given Ant pattern when evaluating
124     * the version.
125     *
126     * @param pattern the Ant pattern
127     * @return this evaluator for chaining
128     */
129    VersionEvaluator matchingAntPattern(String pattern);
130
131    /**
132     * Include all files under the given sub-directory when evaluating
133     * the version.
134     *
135     * @param subDirectory the sub-directory, relative to the repository work
136     * tree or absolute
137     * @return this evaluator for chaining
138     */
139    VersionEvaluator subDirectory(Path subDirectory);
140
141    /**
142     * Returns a stream of "dirty" (uncommitted or untracked) files
143     * in the work tree that match the configured file selection.
144     *
145     * @return a stream of paths
146     */
147    Stream<Path> dirtyFiles();
148
149    /**
150     * Returns a stream of files modified since the latest version tag
151     * that match the configured file selection.
152     *
153     * @return a stream of paths
154     */
155    Stream<Path> modifiedFiles();
156
157    /**
158     * Evaluates and returns the version string for the current repository
159     * state.
160     *
161     * @return the version string
162     */
163    String version();
164}