Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

### Added

- `--version` / `-V` flag prints the CLI version (e.g. `mmcif-dict 0.3.0`) and
exits; the version is sourced from `build.zig.zon` via a build option so it
stays in sync with the release automatically (#36)

## [0.3.0] - 2026-04-16

### Removed
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ The runtime format is a zero-copy native binary (`.mdict`). Produce one with
| `--json` | Output in JSON format |
| `--dict PATH` | Path to dictionary (`.mdict`) |
| `--name NAME` | Select named cache (default: `pdbx`) |
| `--help` | Show usage |
| `--version`, `-V` | Show version and exit |
| `--help`, `-h` | Show usage |

## Environment Variables

Expand Down
7 changes: 7 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
const std = @import("std");
const zon = @import("build.zig.zon");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

// Expose the package version from build.zig.zon as a build option so the
// CLI can print it for --version without hardcoding the string twice.
const options = b.addOptions();
options.addOption([]const u8, "version", zon.version);

const exe = b.addExecutable(.{
.name = "mmcif-dict",
.root_module = b.createModule(.{
Expand All @@ -12,6 +18,7 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
}),
});
exe.root_module.addOptions("build_options", options);
b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);
Expand Down
8 changes: 7 additions & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const build_options = @import("build_options");
const dic_loader = @import("dic_loader.zig");
const dict = @import("dict.zig");
const fetch = @import("fetch.zig");
Expand All @@ -21,7 +22,8 @@ const usage =
\\ --json Output in JSON format
\\ --dict PATH Path to dictionary (.mdict)
\\ --name NAME Select named cache (default: pdbx)
\\ --help Show this help
\\ --version, -V Show version and exit
\\ --help, -h Show this help
\\
\\Environment:
\\ MMCIF_DICT_PATH Default path to dictionary file (.mdict)
Expand Down Expand Up @@ -60,6 +62,10 @@ pub fn main() !void {
try w.writeAll(usage);
try w.flush();
return;
} else if (std.mem.eql(u8, arg, "--version") or std.mem.eql(u8, arg, "-V")) {
try w.print("mmcif-dict {s}\n", .{build_options.version});
try w.flush();
return;
} else if (std.mem.eql(u8, arg, "--dict")) {
i += 1;
if (i >= args.len) {
Expand Down
33 changes: 33 additions & 0 deletions tests/test_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,38 @@ def test_invalid_name_rejected() -> None:
assert "invalid" in r.stderr.lower()


def _expected_version() -> str:
"""Parse the version string out of build.zig.zon without importing Zig."""
text = PROJECT_ROOT.joinpath("build.zig.zon").read_text()
for line in text.splitlines():
stripped = line.strip()
# Match `.version = "X"` exactly; reject siblings like `.version_foo`.
if stripped.startswith(".version") and "=" in stripped and '"' in stripped:
key = stripped.split("=", 1)[0].strip()
if key == ".version":
return stripped.split('"', 2)[1]
raise AssertionError("no .version line in build.zig.zon")


def test_version_long_flag() -> None:
r = _run("--version")
assert r.returncode == 0, r.stderr
assert r.stdout.strip() == f"mmcif-dict {_expected_version()}"


def test_version_short_flag() -> None:
r = _run("-V")
assert r.returncode == 0, r.stderr
assert r.stdout.strip() == f"mmcif-dict {_expected_version()}"


def test_help_mentions_version_flag() -> None:
r = _run("--help")
assert r.returncode == 0, r.stderr
assert "--version" in r.stdout
# Guard against someone quietly dropping the short form from usage text.
assert "-V" in r.stdout


if __name__ == "__main__":
raise SystemExit(pytest.main([__file__, "-v"]))
Loading