Version Comparison
updater.zig does not assume any particular versioning scheme. You can use built-in comparators or create your own.
Built-in Comparators
Semantic Versioning
The default comparator follows Semantic Versioning 2.0.0:
zig
const comparator = updater.version.semantic;Supports:
- Major.Minor.Patch format (e.g.,
1.2.3) - Optional
vprefix (e.g.,v1.2.3) - Prerelease identifiers (e.g.,
1.0.0-alpha.1) - Build metadata (e.g.,
1.0.0+build.123)
Examples:
1.0.0<1.0.1<1.1.0<2.0.01.0.0-alpha<1.0.0-beta<1.0.0v1.0.0==1.0.0
Numeric Versioning
Compares versions as sequences of numbers:
zig
const comparator = updater.version.numeric;Supports:
- Any number of numeric parts separated by dots
- Optional
vprefix
Examples:
1.2<1.10(10 > 2, not lexical)1.2.3.4<1.2.3.5
Lexical Comparison
Simple string comparison (alphabetical order):
zig
const comparator = updater.version.lexical;Examples:
alpha<beta<gammarelease-1<release-2
Date-based Versioning
For calendar versioning (CalVer):
zig
const comparator = updater.version.date_based;Supports formats:
YYYY.MM.DDYYYYMMDD
Examples:
2024.01.01<2024.06.1520240101<20240615
Using Comparators
With Update Checks
zig
const result = try updater.checkForUpdates(allocator, .{
.provider = updater.providers.github,
.owner = "username",
.repo = "project",
.current_version = "1.0.0",
.comparator = updater.version.semantic, // Optional, this is the default
});Direct Comparison
zig
const relation = try updater.version.compareVersions(
allocator,
updater.version.semantic,
"1.0.0", // local version
"2.0.0", // remote version
);
switch (relation) {
.local_newer => std.debug.print("You have a newer version\n", .{}),
.remote_newer => std.debug.print("Update available\n", .{}),
.equal => std.debug.print("Up to date\n", .{}),
.unknown => std.debug.print("Cannot compare versions\n", .{}),
}The VersionRelation Type
zig
pub const VersionRelation = enum {
local_newer, // Current version is newer than remote
remote_newer, // Remote version is newer (update available)
equal, // Versions are the same
unknown, // Cannot determine relationship
pub fn hasUpdate(self: VersionRelation) bool {
return self == .remote_newer;
}
};Creating Custom Comparators
Structure
zig
pub const VersionComparator = struct {
parse: *const fn (allocator: std.mem.Allocator, raw: []const u8) ParseError![]const u8,
compare: *const fn (local: []const u8, remote: []const u8) VersionRelation,
pub const ParseError = error{
InvalidFormat,
OutOfMemory,
};
};Example: Build Number Comparator
zig
const buildNumberComparator = updater.VersionComparator{
.parse = parseBuildNumber,
.compare = compareBuildNumbers,
};
fn parseBuildNumber(allocator: std.mem.Allocator, raw: []const u8) ![]const u8 {
// Format: "build-123" -> "123"
if (!std.mem.startsWith(u8, raw, "build-")) {
return error.InvalidFormat;
}
const number = raw[6..];
// Validate it's a number
_ = std.fmt.parseInt(u64, number, 10) catch return error.InvalidFormat;
return allocator.dupe(u8, number);
}
fn compareBuildNumbers(local: []const u8, remote: []const u8) updater.VersionRelation {
const local_num = std.fmt.parseInt(u64, local, 10) catch return .unknown;
const remote_num = std.fmt.parseInt(u64, remote, 10) catch return .unknown;
if (local_num > remote_num) return .local_newer;
if (local_num < remote_num) return .remote_newer;
return .equal;
}Example: Git Commit Hash Comparator
For projects that version by commit hash, you might want to always show updates:
zig
const commitHashComparator = updater.VersionComparator{
.parse = parseCommitHash,
.compare = compareCommitHashes,
};
fn parseCommitHash(allocator: std.mem.Allocator, raw: []const u8) ![]const u8 {
// Validate it looks like a commit hash
if (raw.len < 7 or raw.len > 40) return error.InvalidFormat;
for (raw) |c| {
if (!std.ascii.isHex(c)) return error.InvalidFormat;
}
return allocator.dupe(u8, raw);
}
fn compareCommitHashes(local: []const u8, remote: []const u8) updater.VersionRelation {
// If hashes are different, assume remote is newer
if (std.mem.eql(u8, local, remote)) return .equal;
return .remote_newer;
}Parsing Semantic Versions Directly
For more control, use SemanticVersion directly:
zig
const v = try updater.version.SemanticVersion.parse("1.2.3-alpha+build.456");
std.debug.print("Major: {d}\n", .{v.major}); // 1
std.debug.print("Minor: {d}\n", .{v.minor}); // 2
std.debug.print("Patch: {d}\n", .{v.patch}); // 3
std.debug.print("Pre: {s}\n", .{v.prerelease.?}); // alpha
std.debug.print("Build: {s}\n", .{v.build_metadata.?}); // build.456
// Compare two versions
const v2 = try updater.version.SemanticVersion.parse("2.0.0");
const relation = v.compare(v2); // .remote_newerBest Practices
- Choose the right comparator: Use semantic for SemVer, numeric for simple versions
- Handle unknown results: Always have a fallback for
.unknown - Test edge cases: Verify your comparator handles all your version formats
- Document your versioning: Make it clear what format you use
Next Steps
- Custom Providers - Create providers for self-hosted servers
- Safety and Opt-out - Privacy controls