use std::fmt; #[derive(Debug, Clone, Copy)] pub struct ErrorCode(pub u16); impl fmt::Display for ErrorCode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "E{:04}", self.0) } } impl std::error::Error for ErrorCode {} impl ErrorCode { pub fn doc_url(&self) -> String { format!( "https://ferrflow.com/docs/reference/errors#{}", self.to_string().to_lowercase() ) } } #[allow(dead_code)] pub trait ErrorCodeExt { fn error_code(self, code: ErrorCode) -> anyhow::Result; } impl ErrorCodeExt for anyhow::Result { fn error_code(self, code: ErrorCode) -> anyhow::Result { self.map_err(|e| e.context(code)) } } #[allow(dead_code)] pub fn code_from_error(err: &anyhow::Error) -> Option { err.downcast_ref::().map(ToString::to_string) } #[allow(dead_code)] pub const CONFIG_NOT_FOUND: ErrorCode = ErrorCode(1001); #[allow(dead_code)] pub const CONFIG_PARSE_JSON: ErrorCode = ErrorCode(1002); #[allow(dead_code)] pub const CONFIG_PARSE_JSON5: ErrorCode = ErrorCode(1003); #[allow(dead_code)] pub const CONFIG_PARSE_TOML: ErrorCode = ErrorCode(1004); #[allow(dead_code)] pub const CONFIG_SERIALIZE_TOML: ErrorCode = ErrorCode(1005); #[allow(dead_code)] pub const CONFIG_PARSE_DOTFILE: ErrorCode = ErrorCode(1006); #[allow(dead_code)] pub const CONFIG_SERIALIZE_DOTFILE: ErrorCode = ErrorCode(1007); #[allow(dead_code)] pub const CONFIG_RESOLVE_PATH: ErrorCode = ErrorCode(1008); #[allow(dead_code)] pub const CONFIG_WRITE_LOADER: ErrorCode = ErrorCode(1009); #[allow(dead_code)] pub const CONFIG_EVAL_TS: ErrorCode = ErrorCode(1010); #[allow(dead_code)] pub const CONFIG_EVAL_NODE: ErrorCode = ErrorCode(1011); #[allow(dead_code)] pub const CONFIG_EVAL_FAILED: ErrorCode = ErrorCode(1012); #[allow(dead_code)] pub const CONFIG_INVALID_OUTPUT: ErrorCode = ErrorCode(1013); #[allow(dead_code)] pub const CONFIG_INVALID_JSON: ErrorCode = ErrorCode(1014); #[allow(dead_code)] pub const CONFIG_READ_FAILED: ErrorCode = ErrorCode(1015); #[allow(dead_code)] pub const CONFIG_MULTIPLE_FILES: ErrorCode = ErrorCode(1016); #[allow(dead_code)] pub const CONFIG_ALREADY_EXISTS: ErrorCode = ErrorCode(1017); #[allow(dead_code)] pub const CONFIG_INVALID_PATH: ErrorCode = ErrorCode(1018); #[allow(dead_code)] pub const CONFIG_INCLUDE_NOT_FOUND: ErrorCode = ErrorCode(1019); #[allow(dead_code)] pub const CONFIG_INCLUDE_INVALID: ErrorCode = ErrorCode(1020); #[allow(dead_code)] pub const CONFIG_INCLUDE_OUTSIDE_ROOT: ErrorCode = ErrorCode(1021); #[allow(dead_code)] pub const CONFIG_DUPLICATE_PACKAGE: ErrorCode = ErrorCode(1022); #[allow(dead_code)] pub const CONFIG_MISSING_PACKAGE_PATH: ErrorCode = ErrorCode(1023); #[allow(dead_code)] pub const VALIDATE_INVALID_REPO_SPEC: ErrorCode = ErrorCode(1100); #[allow(dead_code)] pub const VALIDATE_GITHUB_API: ErrorCode = ErrorCode(1101); #[allow(dead_code)] pub const VALIDATE_GITLAB_API: ErrorCode = ErrorCode(1102); #[allow(dead_code)] pub const VALIDATE_INVALID_UTF8: ErrorCode = ErrorCode(1103); #[allow(dead_code)] pub const VALIDATE_PARSE_FAILED: ErrorCode = ErrorCode(1104); #[allow(dead_code)] pub const VALIDATE_FILE_NOT_FOUND: ErrorCode = ErrorCode(1105); #[allow(dead_code)] pub const VALIDATE_NO_CONFIG: ErrorCode = ErrorCode(1106); #[allow(dead_code)] pub const VALIDATE_REF_REQUIRES_REPO: ErrorCode = ErrorCode(1107); #[allow(dead_code)] pub const GIT_NOT_A_REPO: ErrorCode = ErrorCode(2001); #[allow(dead_code)] pub const GIT_BARE_REPO: ErrorCode = ErrorCode(2002); #[allow(dead_code)] pub const GIT_TAG_EXISTS: ErrorCode = ErrorCode(2003); #[allow(dead_code)] pub const GIT_PUSH_BRANCH: ErrorCode = ErrorCode(2004); #[allow(dead_code)] pub const GIT_PUSH_REJECTED: ErrorCode = ErrorCode(2005); #[allow(dead_code)] pub const GIT_PUSH_TAGS: ErrorCode = ErrorCode(2006); #[allow(dead_code)] pub const GIT_FLOATING_TAGS: ErrorCode = ErrorCode(2007); #[allow(dead_code)] pub const GIT_REMOTE_NOT_FOUND: ErrorCode = ErrorCode(2008); #[allow(dead_code)] pub const GIT_PUSH_VERIFY_FAILED: ErrorCode = ErrorCode(2009); #[allow(dead_code)] pub const GIT_REMOTE_BRANCH_NOT_FOUND: ErrorCode = ErrorCode(2010); #[allow(dead_code)] pub const GIT_LOCKED: ErrorCode = ErrorCode(2011); #[allow(dead_code)] pub const GIT_FORCE_PUSH_BRANCH: ErrorCode = ErrorCode(2012); #[allow(dead_code)] pub const GIT_INSPECT_RELEASE_BRANCH: ErrorCode = ErrorCode(2013); #[allow(dead_code)] pub const GIT_DELETE_TAG: ErrorCode = ErrorCode(2014); #[allow(dead_code)] pub const GIT_REVERT: ErrorCode = ErrorCode(2015); #[allow(dead_code)] pub const GITHUB_CREATE_RELEASE: ErrorCode = ErrorCode(3001); #[allow(dead_code)] pub const GITHUB_LIST_RELEASES: ErrorCode = ErrorCode(3002); #[allow(dead_code)] pub const GITHUB_PARSE_RELEASES: ErrorCode = ErrorCode(3003); #[allow(dead_code)] pub const GITHUB_PUBLISH_RELEASE: ErrorCode = ErrorCode(3004); #[allow(dead_code)] pub const GITHUB_CREATE_PR: ErrorCode = ErrorCode(3005); #[allow(dead_code)] pub const GITHUB_PARSE_PR: ErrorCode = ErrorCode(3006); #[allow(dead_code)] pub const GITHUB_PR_MISSING_FIELD: ErrorCode = ErrorCode(3007); #[allow(dead_code)] pub const GITHUB_AUTO_MERGE: ErrorCode = ErrorCode(3008); #[allow(dead_code)] pub const GITHUB_GRAPHQL_PARSE: ErrorCode = ErrorCode(3009); #[allow(dead_code)] pub const GITHUB_AUTO_MERGE_FAILED: ErrorCode = ErrorCode(3010); #[allow(dead_code)] pub const GITHUB_FIND_PR: ErrorCode = ErrorCode(3011); #[allow(dead_code)] pub const GITHUB_UPDATE_PR: ErrorCode = ErrorCode(3012); #[allow(dead_code)] pub const GITHUB_GRAPHQL_REQUEST: ErrorCode = ErrorCode(3013); #[allow(dead_code)] pub const GITHUB_GRAPHQL_ERROR: ErrorCode = ErrorCode(3014); #[allow(dead_code)] pub const GITHUB_SET_BRANCH: ErrorCode = ErrorCode(3015); #[allow(dead_code)] pub const GITHUB_DELETE_RELEASE: ErrorCode = ErrorCode(3016); #[allow(dead_code)] pub const GITLAB_CREATE_RELEASE: ErrorCode = ErrorCode(3101); #[allow(dead_code)] pub const GITLAB_CREATE_MR: ErrorCode = ErrorCode(3102); #[allow(dead_code)] pub const GITLAB_PARSE_MR: ErrorCode = ErrorCode(3103); #[allow(dead_code)] pub const GITLAB_MR_MISSING_FIELD: ErrorCode = ErrorCode(3104); #[allow(dead_code)] pub const GITLAB_MERGE_MR: ErrorCode = ErrorCode(3105); #[allow(dead_code)] pub const GITLAB_FIND_MR: ErrorCode = ErrorCode(3106); #[allow(dead_code)] pub const GITLAB_UPDATE_MR: ErrorCode = ErrorCode(3107); #[allow(dead_code)] pub const GITEA_CREATE_RELEASE: ErrorCode = ErrorCode(3201); #[allow(dead_code)] pub const GITEA_LIST_RELEASES: ErrorCode = ErrorCode(3202); #[allow(dead_code)] pub const GITEA_PUBLISH_RELEASE: ErrorCode = ErrorCode(3203); #[allow(dead_code)] pub const BITBUCKET_CREATE_RELEASE: ErrorCode = ErrorCode(3301); #[allow(dead_code)] pub const TOML_READ: ErrorCode = ErrorCode(4101); #[allow(dead_code)] pub const TOML_PARSE: ErrorCode = ErrorCode(4102); #[allow(dead_code)] pub const TOML_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4103); #[allow(dead_code)] pub const TOML_WRITE: ErrorCode = ErrorCode(4104); #[allow(dead_code)] pub const TOML_INVALID_UTF8: ErrorCode = ErrorCode(4105); #[allow(dead_code)] pub const JSON_READ: ErrorCode = ErrorCode(4201); #[allow(dead_code)] pub const JSON_PARSE: ErrorCode = ErrorCode(4202); #[allow(dead_code)] pub const JSON_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4203); #[allow(dead_code)] pub const JSON_WRITE: ErrorCode = ErrorCode(4204); #[allow(dead_code)] pub const JSON_INVALID_UTF8: ErrorCode = ErrorCode(4205); #[allow(dead_code)] pub const HELM_READ: ErrorCode = ErrorCode(4301); #[allow(dead_code)] pub const HELM_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4302); #[allow(dead_code)] pub const HELM_WRITE: ErrorCode = ErrorCode(4303); #[allow(dead_code)] pub const HELM_INVALID_UTF8: ErrorCode = ErrorCode(4304); #[allow(dead_code)] pub const XML_READ: ErrorCode = ErrorCode(4401); #[allow(dead_code)] pub const XML_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4402); #[allow(dead_code)] pub const XML_WRITE: ErrorCode = ErrorCode(4403); #[allow(dead_code)] pub const XML_INVALID_UTF8: ErrorCode = ErrorCode(4404); #[allow(dead_code)] pub const CSPROJ_READ: ErrorCode = ErrorCode(4410); #[allow(dead_code)] pub const CSPROJ_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4411); #[allow(dead_code)] pub const CSPROJ_WRITE: ErrorCode = ErrorCode(4412); #[allow(dead_code)] pub const CSPROJ_INVALID_UTF8: ErrorCode = ErrorCode(4413); #[allow(dead_code)] pub const GRADLE_READ: ErrorCode = ErrorCode(4501); #[allow(dead_code)] pub const GRADLE_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4502); #[allow(dead_code)] pub const GRADLE_WRITE: ErrorCode = ErrorCode(4503); #[allow(dead_code)] pub const GRADLE_INVALID_UTF8: ErrorCode = ErrorCode(4504); #[allow(dead_code)] pub const GOMOD_GIT_DESCRIBE: ErrorCode = ErrorCode(4601); #[allow(dead_code)] pub const GOMOD_NO_TAG: ErrorCode = ErrorCode(4602); #[allow(dead_code)] pub const GOMOD_UNSUPPORTED: ErrorCode = ErrorCode(4603); #[allow(dead_code)] pub const TXT_READ: ErrorCode = ErrorCode(4701); #[allow(dead_code)] pub const TXT_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4702); #[allow(dead_code)] pub const TXT_WRITE: ErrorCode = ErrorCode(4703); #[allow(dead_code)] pub const TXT_INVALID_UTF8: ErrorCode = ErrorCode(4704); #[allow(dead_code)] pub const PUBSPEC_READ: ErrorCode = ErrorCode(4801); #[allow(dead_code)] pub const PUBSPEC_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4802); #[allow(dead_code)] pub const PUBSPEC_WRITE: ErrorCode = ErrorCode(4803); #[allow(dead_code)] pub const PUBSPEC_INVALID_UTF8: ErrorCode = ErrorCode(4804); #[allow(dead_code)] pub const MIX_EXS_READ: ErrorCode = ErrorCode(4811); #[allow(dead_code)] pub const MIX_EXS_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4812); #[allow(dead_code)] pub const MIX_EXS_WRITE: ErrorCode = ErrorCode(4813); #[allow(dead_code)] pub const MIX_EXS_INVALID_UTF8: ErrorCode = ErrorCode(4814); #[allow(dead_code)] pub const CHART_YAML_READ: ErrorCode = ErrorCode(4821); #[allow(dead_code)] pub const CHART_YAML_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4822); #[allow(dead_code)] pub const CHART_YAML_WRITE: ErrorCode = ErrorCode(4823); #[allow(dead_code)] pub const CHART_YAML_INVALID_UTF8: ErrorCode = ErrorCode(4824); #[allow(dead_code)] pub const GEMSPEC_READ: ErrorCode = ErrorCode(4831); #[allow(dead_code)] pub const GEMSPEC_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4832); #[allow(dead_code)] pub const GEMSPEC_WRITE: ErrorCode = ErrorCode(4833); #[allow(dead_code)] pub const GEMSPEC_INVALID_UTF8: ErrorCode = ErrorCode(4834); #[allow(dead_code)] pub const PACKAGE_SWIFT_READ: ErrorCode = ErrorCode(4841); #[allow(dead_code)] pub const PACKAGE_SWIFT_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4842); #[allow(dead_code)] pub const PACKAGE_SWIFT_WRITE: ErrorCode = ErrorCode(4843); #[allow(dead_code)] pub const PACKAGE_SWIFT_INVALID_UTF8: ErrorCode = ErrorCode(4844); #[allow(dead_code)] pub const CABAL_READ: ErrorCode = ErrorCode(4851); #[allow(dead_code)] pub const CABAL_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4852); #[allow(dead_code)] pub const CABAL_WRITE: ErrorCode = ErrorCode(4853); #[allow(dead_code)] pub const CABAL_INVALID_UTF8: ErrorCode = ErrorCode(4854); #[allow(dead_code)] pub const CMAKE_READ: ErrorCode = ErrorCode(4861); #[allow(dead_code)] pub const CMAKE_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4862); #[allow(dead_code)] pub const CMAKE_WRITE: ErrorCode = ErrorCode(4863); #[allow(dead_code)] pub const CMAKE_INVALID_UTF8: ErrorCode = ErrorCode(4864); #[allow(dead_code)] pub const PRERELEASE_EMPTY_CHANNEL: ErrorCode = ErrorCode(5001); #[allow(dead_code)] pub const PRERELEASE_INVALID_CHANNEL: ErrorCode = ErrorCode(5002); #[allow(dead_code)] pub const VERSIONING_INVALID_SEMVER: ErrorCode = ErrorCode(5010); #[allow(dead_code)] pub const HOOK_FAILED: ErrorCode = ErrorCode(6001); #[allow(dead_code)] pub const QUERY_NO_PACKAGES: ErrorCode = ErrorCode(7001); #[allow(dead_code)] pub const QUERY_PACKAGE_NOT_FOUND: ErrorCode = ErrorCode(7002); #[allow(dead_code)] pub const DIFF_BAD_RANGE: ErrorCode = ErrorCode(7003); #[allow(dead_code)] pub const DIFF_PACKAGE_REQUIRED: ErrorCode = ErrorCode(7004); #[allow(dead_code)] pub const DIFF_ENDPOINT_UNRESOLVED: ErrorCode = ErrorCode(7005); #[allow(dead_code)] pub const MONOREPO_PACKAGE_NOT_FOUND: ErrorCode = ErrorCode(8001); #[allow(dead_code)] pub const MONOREPO_PUSH_FAILED: ErrorCode = ErrorCode(8002); #[allow(dead_code)] pub const MONOREPO_DEPENDENCY_CYCLE: ErrorCode = ErrorCode(8003); #[cfg(test)] mod tests { use super::*; #[test] fn display_formats_with_leading_zeros() { assert_eq!(ErrorCode(1).to_string(), "E0001"); assert_eq!(ErrorCode(42).to_string(), "E0042"); assert_eq!(ErrorCode(1001).to_string(), "E1001"); assert_eq!(ErrorCode(9999).to_string(), "E9999"); } #[test] fn doc_url_uses_lowercase() { let url = ErrorCode(1001).doc_url(); assert_eq!(url, "https://ferrflow.com/docs/reference/errors#e1001"); } #[test] fn error_code_ext_attaches_code() { let err: anyhow::Result<()> = Err(anyhow::anyhow!("something broke")); let err = err.error_code(ErrorCode(2001)); let err = err.unwrap_err(); let code = err.downcast_ref::().copied(); assert!(code.is_some()); assert_eq!(code.unwrap().0, 2001); } #[test] fn error_without_code_returns_none() { let err = anyhow::anyhow!("plain error"); let code = err.downcast_ref::().copied(); assert!(code.is_none()); } #[test] fn code_from_error_finds_wrapped_code() { let err = anyhow::anyhow!("boom") .context(ErrorCode(2004)) .context("while pushing"); assert_eq!(code_from_error(&err).as_deref(), Some("E2004")); } #[test] fn code_from_error_none_when_absent() { let err = anyhow::anyhow!("plain").context("still plain"); assert!(code_from_error(&err).is_none()); } } #[allow(dead_code)] pub const ROLLBACK_BLOCKED: ErrorCode = ErrorCode(10000);