diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-22 00:37:54 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-22 00:37:54 +0900 |
| commit | 0a8e5935e6305819bb02d8c69e2f046ff397913a (patch) | |
| tree | e5a288e679477b1603d7989e986ca22bbe590aa4 /crates/mozart-core/src/exit_code.rs | |
| parent | b5af594fec7da72b15c9a202c641af0494db6355 (diff) | |
| download | php-mozart-0a8e5935e6305819bb02d8c69e2f046ff397913a.tar.gz php-mozart-0a8e5935e6305819bb02d8c69e2f046ff397913a.tar.zst php-mozart-0a8e5935e6305819bb02d8c69e2f046ff397913a.zip | |
refactor(workspace): split monolithic crate into 6 workspace crates
Extract modules from the single `mozart` crate into 5 focused library
crates to improve compilation parallelism and architectural clarity:
- mozart-constraint: version constraint parser (independent)
- mozart-core: base types, console, validation, platform utilities
- mozart-archiver: archive creation (tar, zip, bzip2)
- mozart-registry: Packagist API, cache, resolver, downloader, lockfile
- mozart-autoload: autoloader generation and PHP scanner
Refactor Console::from_cli and build_cache_config to accept primitive
args instead of &Cli to break circular dependencies. Introduce
[workspace.dependencies] for centralized version management. Remove 9
unused direct dependencies from the CLI crate.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-core/src/exit_code.rs')
| -rw-r--r-- | crates/mozart-core/src/exit_code.rs | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/crates/mozart-core/src/exit_code.rs b/crates/mozart-core/src/exit_code.rs new file mode 100644 index 0000000..bc01cfa --- /dev/null +++ b/crates/mozart-core/src/exit_code.rs @@ -0,0 +1,114 @@ +/// Exit code: success. +pub const OK: i32 = 0; + +/// Exit code: general / unclassified error. +pub const GENERAL_ERROR: i32 = 1; + +/// Exit code: dependency resolution failed. +pub const DEPENDENCY_RESOLUTION_FAILED: i32 = 2; + +/// Exit code: partial update requested but no lock file exists. +pub const NO_LOCK_FILE_FOR_PARTIAL_UPDATE: i32 = 3; + +/// Exit code: lock file is invalid or corrupt. +pub const LOCK_FILE_INVALID: i32 = 4; + +/// Exit code: audit found a security advisory. +pub const AUDIT_FAILED: i32 = 5; + +/// Exit code: HTTP / network transport error. +pub const TRANSPORT_ERROR: i32 = 100; + +// --------------------------------------------------------------------------- +// MozartError — carries a specific exit code through anyhow's error chain +// --------------------------------------------------------------------------- + +/// An error type that carries a specific exit code for Mozart to use on exit. +/// +/// Use [`bail`] or [`bail_silent`] to construct one wrapped in `anyhow::Error`. +#[derive(Debug)] +pub struct MozartError { + pub message: String, + pub exit_code: i32, +} + +impl std::fmt::Display for MozartError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.message) + } +} + +impl std::error::Error for MozartError {} + +/// Return an `anyhow::Error` that carries `exit_code` and prints `message`. +pub fn bail(exit_code: i32, message: impl Into<String>) -> anyhow::Error { + MozartError { + message: message.into(), + exit_code, + } + .into() +} + +/// Return an `anyhow::Error` that carries `exit_code` but suppresses the +/// message (caller has already printed it). +pub fn bail_silent(exit_code: i32) -> anyhow::Error { + MozartError { + message: String::new(), + exit_code, + } + .into() +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_constants_have_expected_values() { + assert_eq!(OK, 0); + assert_eq!(GENERAL_ERROR, 1); + assert_eq!(DEPENDENCY_RESOLUTION_FAILED, 2); + assert_eq!(NO_LOCK_FILE_FOR_PARTIAL_UPDATE, 3); + assert_eq!(LOCK_FILE_INVALID, 4); + assert_eq!(AUDIT_FAILED, 5); + assert_eq!(TRANSPORT_ERROR, 100); + } + + #[test] + fn test_mozart_error_display() { + let err = MozartError { + message: "something went wrong".to_string(), + exit_code: GENERAL_ERROR, + }; + assert_eq!(format!("{err}"), "something went wrong"); + } + + #[test] + fn test_bail_can_be_downcast() { + let err = bail(DEPENDENCY_RESOLUTION_FAILED, "cannot resolve"); + let me = err.downcast_ref::<MozartError>().expect("should downcast"); + assert_eq!(me.exit_code, DEPENDENCY_RESOLUTION_FAILED); + assert_eq!(me.message, "cannot resolve"); + } + + #[test] + fn test_bail_silent_has_empty_message() { + let err = bail_silent(GENERAL_ERROR); + let me = err.downcast_ref::<MozartError>().expect("should downcast"); + assert_eq!(me.exit_code, GENERAL_ERROR); + assert!(me.message.is_empty()); + } + + #[test] + fn test_mozart_error_is_std_error() { + let err: Box<dyn std::error::Error> = Box::new(MozartError { + message: "test".to_string(), + exit_code: 1, + }); + assert_eq!(err.to_string(), "test"); + } +} |
