Skip to content

Architecture

Cursus is a Rust CLI application built with clap for argument parsing and ratatui for terminal UI. This page gives a high-level tour of the codebase — for detailed design rationale, see the Architecture Decision Records.

ModuleResponsibility
src/cli/clap-based CLI with global flags and subcommands
src/tui/Interactive terminal UI wizards (ratatui/crossterm)
src/model/Core domain types: config, changesets, changelogs
src/package_manager/Adapter trait for Cargo and npm workspace enumeration, versioning, and publishing
src/git/Git lifecycle management (commit, tag, push, branch)
src/github/GitHub API integration (releases, PRs, asset uploads)
src/command/CommandRunner trait for shell command execution with dry-run support
src/env.rsDependency injection and runner composition
src/conventional_commit.rsConventional Commit parser
src/path.rsAbsolutePath newtype for validated absolute paths

The PackageManagerAdapter trait provides a uniform interface across package managers:

  • enumerate_projects() — discover packages and their current versions
  • write_version() — update a package’s version in its manifest; returns Vec<PathBuf> of the files actually modified (which may be the workspace-root manifest rather than the package’s own, e.g. for Cargo crates using version.workspace = true); callers must stage those paths for git
  • update_dependency_version() — update an intra-workspace dependency’s pinned version; also returns Vec<PathBuf> of modified files for git staging
  • update_lock_file() — regenerate the lock file after version changes
  • publish() — publish a package to its registry
  • registry_name() — display name for the registry

Each TUI wizard follows a consistent pattern:

  • A Screen enum tracks the current state
  • A pure handle_key() function handles state transitions (testable without a terminal)
  • Separate ui() / render_*() functions handle rendering

Every GitWorkdir method that accepts a caller-supplied branch name, tag name, or revision string passes it through the appropriate validator in the git::ref_format submodule (validate_branch_name, validate_tag_name, validate_revision) before invoking the CommandRunner. Any new git operation that takes such a string must do the same.

The CommandRunner trait abstracts shell command execution. The DryRunCommandRunner decorator wraps any runner and intercepts commands that would mutate state, logging them instead — implementing a late-guard dry-run pattern.

Cursus uses anyhow::Result throughout. Production code never panics — all errors are propagated with .context() or bail!().

For detailed type-level documentation, see the rustdoc on docs.rs.