# error-path-macros
`error-path-macros`는 `Result` 기반 Rust 함수와 `impl` 메서드에서 오류가 반환될 때 운영용 주소 조각을 자동으로 추가하는 절차적 속성 매크로 crate입니다. 주소는 호출 순서를 복원하는 stack trace가 아니라, 오류가 시스템의 어느 기능에 속하는지 나타내는 안정적인 식별자입니다.
```text
login.api.request_login
```
대부분의 애플리케이션은 이 crate를 직접 의존하지 않아도 됩니다. [`error-path`](https://crates.io/crates/error-path)의 `macros` feature가 같은 매크로를 재노출하며, 필요한 `WithErrorPath` trait과 adapter도 함께 제공합니다.
## 권장 설치
```toml
[dependencies]
error-path = { version = "0.1", features = ["macros"] }
```
## 직접 설치
매크로를 별도 crate에서 재노출하거나 의존성을 세밀하게 구성해야 한다면 core crate와 함께 설치합니다.
```toml
[dependencies]
error-path = "0.1"
error-path-macros = "0.1"
```
생성된 코드는 `::error_path::WithErrorPath`를 참조합니다. 따라서 core crate가 반드시 필요하며, 의존성 이름을 바꿨다면 `error_path`라는 이름으로 다시 노출해야 합니다. 최소 지원 Rust 버전은 1.77입니다.
## `#[error_path]`
단일 `Result` 반환 함수에 주소 조각을 붙입니다. 인자가 없으면 함수 이름을 사용하고, 문자열 인자가 있으면 그 값을 사용합니다. 경로 문자열 해석은 `error-path`의 현재 전역 구분자를 따릅니다.
```rust
use error_path::{ErrorPath, WithErrorPath};
use error_path_macros::error_path;
#[derive(Debug)]
struct ProfileError { path: ErrorPath }
impl WithErrorPath for ProfileError {
fn with_error_path(mut self, path: &'static str) -> Self {
self.path.prepend_path(path);
self
}
}
#[error_path]
fn load_profile() -> Result<(), ProfileError> {
Err(ProfileError { path: ErrorPath::new() })
}
#[error_path("login.api")]
fn request_login() -> Result<(), ProfileError> {
load_profile()
}
```
개념적으로 매크로는 원래 함수 본문을 실행한 뒤 다음과 같은 처리를 추가합니다.
```rust
result.map_err(|error| error_path::WithErrorPath::with_error_path(error, "login.api"))
```
동기와 `async` 함수 모두 지원하며, 원래 함수의 visibility와 signature는 유지합니다.
## `#[error_path_impl]`
`impl` 블록의 `Result` 반환 메서드에 `{base}.{method_name}` 주소를 붙입니다.
```rust
use error_path::{ErrorPath, WithErrorPath};
use error_path_macros::{error_path_impl, error_path_skip};
struct LoginApi;
#[error_path_impl("login.api")]
impl LoginApi {
fn request_login(&self) -> Result<(), ProfileError> {
Err(ProfileError { path: ErrorPath::new() })
}
#[error_path_skip]
fn health_check(&self) -> Result<(), ProfileError> {
Ok(())
}
}
```
인자가 없을 때 base는 다음 규칙으로 정해집니다.
- trait implementation: trait 이름
- inherent implementation: 타입 이름
예를 들어 `impl LoginApi for LoginApiImpl`에 `#[error_path_impl]`을 붙이면 `LoginApi.request_login`이 생성됩니다. 별칭을 사용하고 싶다면 `#[error_path_impl("login.api")]`처럼 명시합니다.
## `#[error_path_skip]`
`#[error_path_impl]`이 붙은 블록에서 특정 메서드를 그대로 두고 싶을 때 사용합니다. 이 속성은 `#[error_path_impl]`이 소비하므로 단독으로 경로를 추가하지 않습니다.
## 오류 타입 호환성
매크로는 특정 에러 타입을 알지 못하며 `WithErrorPath` 하나만 호출합니다. 따라서 함수가 `Result<T, E>`를 반환하고 `E: WithErrorPath`이면 사용할 수 있습니다.
`error-path` core crate는 `ErrorPath`와 `anyhow`, `eyre`, `error-stack`, `stacked_errors`용 선택 adapter를 제공합니다. 자체 오류 타입이나 `thiserror`로 만든 타입도 `WithErrorPath`만 구현하면 호환됩니다.
## 제한사항
- `#[error_path]`는 `Result` 반환 함수에만 적용해야 합니다. 다른 반환 타입에 적용하면 생성 코드가 `map_err`를 호출하므로 컴파일되지 않습니다.
- `#[error_path_impl]`은 반환 타입의 마지막 이름이 `Result`로 끝나는 메서드만 자동으로 감쌉니다.
- panic, thread, spawn 내부 오류는 자동 대상이 아닙니다. `Result`가 현재 함수 경계를 통해 반환될 때만 주소가 추가됩니다.
- 주소는 source file 경로나 runtime stack trace에서 추론되지 않습니다. 안정적인 문자열을 직접 제공하거나 함수·trait·타입 이름 기반 기본값을 사용합니다.
- 매크로는 `::error_path` 경로를 사용하므로, core crate dependency rename에는 위의 재노출 구성이 필요합니다.
## 관련 문서
- [error-path core crate](https://crates.io/crates/error-path)
- [프로젝트 문서와 예제](https://github.com/yongaru/error-path-kit)
- [API 문서](https://docs.rs/error-path-macros)
- [변경 기록](https://github.com/yongaru/error-path-kit/blob/main/CHANGELOG.md)
## 라이선스
Apache-2.0 또는 MIT 라이선스 중 하나를 선택해 사용할 수 있습니다.
## 만든이
용사장이 만들고 관리합니다. [akira76@gmail.com](mailto:akira76@gmail.com)