Java implementation of vers, a mostly universal version range specifier
| Scheme | Supported |
|---|---|
| Alpine | ❌ |
| CPAN | ❌ |
| Debian | ✅ |
| Ruby Gem | ❌ |
| Generic | ✅ |
| Gentoo | ❌ |
| Go | ✅ |
| Maven | ✅ |
| NPM | ✅ |
| Nuget | ❌ |
| PyPI | ❌ |
| RPM | ✅ |
Note
Support for new schemes can be added, and default implementations overwritten, by extending versatile!
Versions for which the appropriate scheme is not currently supported will fall back to generic.
<dependency>
<groupId>io.github.nscuro</groupId>
<artifactId>versatile-core</artifactId>
<version>${versatile.version}</version>
</dependency>Note
versatile requires Java >= 17.
Ranges are constructed using a builder. Builders must be initialized with a versioning scheme.
Constraints may be provided in structured, or freeform format. Versions used in constraints must
be valid according to the chosen versioning scheme. When VersBuilder#build is called, constraints are sorted
by version, and the built Vers is validated. If the range turns out to be invalid, an VersException is thrown.
import io.github.nscuro.versatile.Comparator;
import io.github.nscuro.versatile.Vers;
import static io.github.nscuro.versatile.version.KnownVersioningSchemes.SCHEME_GOLANG;
class ConstructVers {
void shouldConstructVers() {
Vers vers = Vers.builder(SCHEME_GOLANG)
.withConstraint(Comparator.GREATER_THAN, "v1.2.3")
.withConstraint(Comparator.LESS_THAN_OR_EQUAL, "v3.2.1")
.withConstraint("!= v2.1.3")
.build();
assert "vers:golang/>v1.2.3|!=v2.1.3|<=v3.2.1".equals(vers.toString());
}
}vers ranges may
be parsed
using the Vers#parse method. If the range turns out to be invalid, a VersException is thrown.
import io.github.nscuro.versatile.Vers;
import static io.github.nscuro.versatile.version.KnownVersioningSchemes.SCHEME_GOLANG;
class ParseVers {
void shouldParseVers() {
Vers vers = Vers.parse("vers:golang/>v1.2.3|!=v2.1.3|<=v3.2.1");
assert SCHEME_GOLANG.equals(vers.scheme());
assert vers.constraints().size() == 3;
}
}The vers specification defines an algorithm
to simplify
constraints in a range. This mechanism is exposed through the Vers#simplify method.
import io.github.nscuro.versatile.Vers;
class SimplifyVers {
void shouldSimplify() {
Vers vers = Vers.parse("vers:golang/>v0.0.0|>=v0.0.1|v0.0.2|<v0.0.3|v0.0.4|<v0.0.5|>=v0.0.6");
assert "vers:golang/>v0.0.0|<v0.0.5|>=v0.0.6".equals(vers.simplify().toString());
}
}Note
versatile will never simplify ranges on its own. If simplification is desired,simplifymust be called explicitly.
To check whether a given vers
range contains
a specific version, the Vers#contains method may be used. The provided version must be valid according
to the range's versioning scheme.
import io.github.nscuro.versatile.Vers;
class VersContains {
Vers vers = Vers.parse("vers:golang/>v1.2.3|!=v2.1.3|<=v3.2.1");
void shouldContainVersion() {
assert vers.contains("v1.2.4");
assert vers.contains("v2.0.2");
assert vers.contains("v3.2.1");
}
void shouldNotContainVersion() {
assert !vers.contains("v1.2.3");
assert !vers.contains("v2.1.3");
assert !vers.contains("v3.2.2");
}
}Versions can be used directly, outside the context of a vers range. To acquire a Version object,
VersionFactory may be used:
import io.github.nscuro.versatile.VersionFactory;
import io.github.nscuro.versatile.version.GoVersion;
import io.github.nscuro.versatile.version.Version;
import static io.github.nscuro.versatile.version.KnownVersioningSchemes.SCHEME_GOLANG;
class VersContains {
void shouldReturnGoVersion() {
Version version = VersionFactory.getVersion(SCHEME_GOLANG, "v1.2.4");
assert version instanceof GoVersion;
assert "golang".equals(version.scheme());
}
void shouldFallbackToGenericVersion() {
Version version = VersionFactory.getVersion("foobar", "v1.2.4");
assert version instanceof GenericVersion;
assert "foobar".equals(version.scheme());
}
}As shown above, if versatile doesn't recognize the provided versioning scheme, it will fall back
to GenericVersion. Support for additional schemes can be added by extending versatile.
versatile ships with support for a few versioning schemes (see Supported Versioning Schemes).
While contributions to add support for more schemes is highly appreciated, it's not always feasible to wait for changes
to be released. It should be possible to leverage versatile's vers functionality, without being reliant on
how fast support for new schemes is added upstream.
On the other hand, the default implementations may not always align with the desired behavior. Perhaps they are too strict, and a more lax parsing logic is required. In that case, the default will need to be overwritten.
To address these concerns, versatile exposes an SPI for versioning scheme support.
To add support for a new scheme, let's say alpine, the following steps may be performed:
- Add either
versatile-core, orversatile-spias dependency to your project - Create a class
AlpineVersionthat extendsio.github.nscuro.versatile.version.Version - Implement the version parsing logic as desired
- Be sure to overwrite
compareTo,equals,hashCode, andtoString
- Be sure to overwrite
- Create a class
AlpineVersionProviderthat implementsio.github.nscuro.versatile.version.VersionProvider- Implement
#supportsScheme(String scheme)to returntruefor thealpinescheme - Implement
#getVersion(String scheme, String verstionStr)to return an instance ofAlpineVersion - Implement
#priority()to return a value between0(lowest), andInteger.MAX_VALUE(highest)- Built-in providers have a priority of
50(VersionProvider#PRIORITY_BUILTIN) - By defining a priority higher than
50, built-in providers can effectively be overwritten
- Built-in providers have a priority of
- Implement
- Create a file
src/main/resources/META-INF/services/io.github.nscuro.versatile.version.VersionProvider - List the fully qualified package name of all custom
VersionProviderimplementations, one per line- e.g.
com.acme.AlpineVersionProvider
- e.g.
That's it! Now, whenever versatile encounters a vers range with the scheme alpine, it will use your AlpineVersion!