Skip to content

Commit 2af88e9

Browse files
committed
fix: convert test
1 parent 2c25477 commit 2af88e9

File tree

7 files changed

+648
-71
lines changed

7 files changed

+648
-71
lines changed

.hooks/pre-commit

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
#!/usr/bin/env bash
22
set -euo pipefail
33

4-
# Ensure nightly rustfmt is available
4+
echo "🔧 Ensuring nightly rustfmt is available..."
55
if ! rustup toolchain list | grep -q "^nightly"; then
66
rustup toolchain install nightly >/dev/null
77
fi
88
rustup component add rustfmt --toolchain nightly >/dev/null
99

10-
echo "🧹 Running cargo fmt check..."
10+
echo "🧹 Checking formatting with rustfmt..."
1111
cargo +nightly fmt --all -- --check
1212

13-
echo "🧐 Running cargo clippy..."
13+
echo "🔍 Running clippy (all features, all targets)..."
1414
cargo clippy --workspace --all-targets --all-features -- -D warnings
1515

16-
# echo "📦 Running sqlx prepare..."
16+
echo "🧪 Running tests (all features)..."
17+
cargo test --workspace --all-features
18+
19+
# Uncomment if you want to validate SQLx offline data
20+
# echo "📦 Validating SQLx prepare..."
1721
# cargo sqlx prepare --check --workspace
1822

1923
echo "✅ All checks passed!"
24+

Makefile.toml

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
# Makefile.toml for cargo-make
22
# Usage:
3-
# cargo make ci
4-
# TAG=v0.1.1 cargo make release
5-
# CARGO_REGISTRY_TOKEN=... TAG=v0.1.1 cargo make publish
6-
7-
[config]
8-
default_task = "ci"
3+
# cargo make # pretty help (default)
4+
# cargo make ci # fmt, clippy, tests, package
5+
# TAG=v0.3.0 cargo make release
6+
# CARGO_REGISTRY_TOKEN=... TAG=v0.3.0 cargo make publish
97

108
[env]
11-
ALL_FEATURES = "true"
9+
ALL_FEATURES = "true" # toggle --all-features for clippy/test
10+
11+
# ------- Default task (no root 'default_task' key) -------
12+
13+
[tasks.default]
14+
category = "Meta"
15+
description = "Default task -> help"
16+
run_task = "help"
1217

1318
# ------- Core checks -------
1419

@@ -22,29 +27,29 @@ description = "Check rustfmt formatting for the entire workspace"
2227
[tasks.clippy]
2328
category = "Lint"
2429
clear = true
30+
script_runner = "bash"
2531
script = ['''
26-
#!/usr/bin/env bash
2732
set -euo pipefail
2833
if [ "${ALL_FEATURES}" = "true" ]; then
2934
cargo clippy --workspace --all-targets --all-features -- -D warnings
3035
else
3136
cargo clippy --workspace --all-targets -- -D warnings
3237
fi
33-
''']
38+
''']
3439
description = "Run clippy for all targets; fail on warnings"
3540

3641
[tasks.test]
3742
category = "Test"
3843
clear = true
44+
script_runner = "bash"
3945
script = ['''
40-
#!/usr/bin/env bash
4146
set -euo pipefail
4247
if [ "${ALL_FEATURES}" = "true" ]; then
4348
cargo test --workspace --all-features --no-fail-fast
4449
else
4550
cargo test --workspace --no-fail-fast
4651
fi
47-
''']
52+
''']
4853
description = "Run tests (optionally with all features)"
4954

5055
[tasks.package]
@@ -63,28 +68,28 @@ description = "Run fmt, clippy, tests, and packaging checks"
6368

6469
[tasks.check_tag_env]
6570
clear = true
71+
script_runner = "bash"
6672
script = ['''
67-
#!/usr/bin/env bash
6873
set -euo pipefail
6974
if [ -z "${TAG:-}" ]; then
70-
echo "TAG env var is required, e.g. TAG=v0.1.1"
75+
echo "TAG env var is required, e.g. TAG=v0.3.0"
7176
exit 1
7277
fi
73-
''']
78+
''']
7479
description = "Ensure TAG env var is provided (e.g. TAG=v1.2.3)"
7580

7681
[tasks.ensure_tag_matches_version]
7782
clear = true
83+
script_runner = "bash"
7884
script = [
7985
'''
80-
#!/usr/bin/env bash
8186
set -euo pipefail
8287
TAG="${TAG#refs/tags/}"
8388
8489
if command -v jq >/dev/null 2>&1; then
8590
FILE_VER="$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].version')"
8691
else
87-
FILE_VER="$(cargo metadata --no-deps --format-version=1 | sed -n 's/.*\"version\":\"\\([^\"]*\\)\".*/\\1/p' | head -n1)"
92+
FILE_VER="$(cargo metadata --no-deps --format-version=1 | sed -n 's/.*"version":"\([^"]*\)".*/\1/p' | head -n1)"
8893
fi
8994
9095
if [ -z "${FILE_VER}" ]; then
@@ -98,48 +103,48 @@ script = [
98103
fi
99104
100105
echo "Tag ${TAG} matches Cargo.toml version v${FILE_VER}"
101-
''',
106+
''',
102107
]
103108
dependencies = ["check_tag_env"]
104109
description = "Ensure git tag matches Cargo.toml version"
105110

106111
[tasks.check_token]
107112
clear = true
113+
script_runner = "bash"
108114
script = ['''
109-
#!/usr/bin/env bash
110115
set -euo pipefail
111116
if [ -z "${CARGO_REGISTRY_TOKEN:-}" ]; then
112117
echo "CARGO_REGISTRY_TOKEN is required to publish."
113118
exit 1
114119
fi
115-
''']
120+
''']
116121
description = "Ensure crates.io token is present in env"
117122

118123
# ------- Publish & Release -------
119124

120125
[tasks.publish]
121126
category = "Release"
122127
clear = true
128+
script_runner = "bash"
123129
script = ['''
124-
#!/usr/bin/env bash
125130
set -euo pipefail
126131
cargo publish --locked --token "${CARGO_REGISTRY_TOKEN}"
127-
''']
132+
''']
128133
dependencies = ["check_token", "ensure_tag_matches_version", "ci"]
129134
description = "Publish crate to crates.io after checks and tag/version verification"
130135

131136
[tasks.tag]
132137
category = "Release"
133138
clear = true
139+
script_runner = "bash"
134140
script = [
135141
'''
136-
#!/usr/bin/env bash
137142
set -euo pipefail
138143
139144
if command -v jq >/dev/null 2>&1; then
140145
VER="$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].version')"
141146
else
142-
VER="$(cargo metadata --no-deps --format-version=1 | sed -n 's/.*\"version\":\"\\([^\"]*\\)\".*/\\1/p' | head -n1)"
147+
VER="$(cargo metadata --no-deps --format-version=1 | sed -n 's/.*"version":"\([^"]*\)".*/\1/p' | head -n1)"
143148
fi
144149
145150
if [ -z "${VER}" ]; then
@@ -156,7 +161,7 @@ script = [
156161
157162
git tag "${TAG}"
158163
echo "Created tag ${TAG}. You can push it with: git push origin ${TAG}"
159-
''',
164+
''',
160165
]
161166
description = "Create git tag vX.Y.Z from Cargo.toml version (does not push)"
162167

@@ -190,21 +195,16 @@ description = "Install git pre-commit hook from .hooks/"
190195
script_runner = "bash"
191196
script = [
192197
'set -euo pipefail',
193-
'if [ ! -f .hooks/pre-commit ]; then echo "❌ .hooks/pre-commit не найден!"; exit 1; fi',
198+
'if [ ! -f .hooks/pre-commit ]; then echo "❌ .hooks/pre-commit not found!"; exit 1; fi',
194199
'echo "🔗 Linking .hooks/pre-commit to .git/hooks/pre-commit..."',
195200
'mkdir -p .git/hooks',
196201
'ln -sf ../../.hooks/pre-commit .git/hooks/pre-commit',
197202
'chmod +x .hooks/pre-commit',
198-
'echo "✅ pre-commit hook установлен."',
203+
'echo "✅ pre-commit hook installed."',
199204
]
200205

201206
# ------- Pretty Help -------
202207

203-
[tasks.default]
204-
category = "Meta"
205-
description = "Default task -> ci"
206-
run_task = "ci"
207-
208208
[tasks.help]
209209
clear = true
210210
category = "Meta"
@@ -214,7 +214,6 @@ script = [
214214
'''
215215
set -euo pipefail
216216
217-
# Enable colors if terminal supports it and NO_COLOR is not set
218217
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
219218
BOLD="\033[1m"; DIM="\033[2m"; RESET="\033[0m"
220219
BLUE="\033[34m"; CYAN="\033[36m"; GREEN="\033[32m"; YELLOW="\033[33m"; MAGENTA="\033[35m"
@@ -223,14 +222,13 @@ script = [
223222
fi
224223
225224
hr() { printf "%s\n" "──────────────────────────────────────────────────────────────────────────────"; }
226-
pad() { printf "%-*s" "$2" "$1"; }
227225
row() { printf " %b%s%b %b%s%b\n" "$GREEN" "$1" "$RESET" "$DIM" "$2" "$RESET"; }
228226
229-
# Extract crate info
230227
if command -v jq >/dev/null 2>&1; then
231-
VER="$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].version')"
232-
MSRV="$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].rust_version // "unknown"')"
233-
NAME="$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].name')"
228+
META="$(cargo metadata --no-deps --format-version=1)"
229+
VER="$(printf "%s" "$META" | jq -r '.packages[0].version')"
230+
MSRV="$(printf "%s" "$META" | jq -r '.packages[0].rust_version // "unknown"')"
231+
NAME="$(printf "%s" "$META" | jq -r '.packages[0].name')"
234232
else
235233
META="$(cargo metadata --no-deps --format-version=1)"
236234
VER="$(printf "%s" "$META" | sed -n 's/.*\"version\":\"\\([^\"]*\\)\".*/\\1/p' | head -n1)"
@@ -255,7 +253,7 @@ script = [
255253
256254
printf "%b%s%b\n" "$BOLD$MAGENTA" "Formatting" "$RESET"
257255
row "cargo make fmt" "Check formatting (stable)"
258-
row "cargo make format" "Format with nightly rustfmt (unstable_features)"
256+
row "cargo make format" "Format with nightly rustfmt (unstable features)"
259257
echo
260258
261259
printf "%b%s%b\n" "$BOLD$MAGENTA" "Lint & Tests" "$RESET"
@@ -279,15 +277,15 @@ script = [
279277
echo
280278
281279
printf "%b%s%b\n" "$BOLD$DIM" "Tips:" "$RESET"
282-
printf " • Keep %bCargo.lock%b committed (CI uses %bCARGO_LOCKED=1%b)\n" "$BOLD" "$RESET" "$BOLD" "$RESET"
280+
printf " • Keep %bCargo.lock%b committed\n" "$BOLD" "$RESET"
283281
printf " • Tag must exactly match version: %bv%s%b\n" "$BOLD" "${VER}" "$RESET"
284282
echo
285-
''',
283+
''',
286284
]
287285

288-
286+
# Alias
289287
[tasks.h]
290288
clear = true
291289
run_task = "help"
292-
description = "Alias: help"
293290
category = "Meta"
291+
description = "Alias: help"

README.md

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Small, pragmatic error model for API-heavy Rust services.
1010
Core is framework-agnostic; integrations are opt-in via feature flags.
1111
Stable categories, conservative HTTP mapping, no `unsafe`.
1212

13-
- Core types: `AppError`, `AppErrorKind`, `AppResult`, `ErrorResponse`
13+
- Core types: `AppError`, `AppErrorKind`, `AppResult`, `AppCode`, `ErrorResponse`
1414
- Optional Axum/Actix integration
1515
- Optional OpenAPI schema (via `utoipa`)
1616
- Conversions from `sqlx`, `reqwest`, `redis`, `validator`, `config`, `tokio`
@@ -27,23 +27,26 @@ Stable categories, conservative HTTP mapping, no `unsafe`.
2727
- `serde_json` (JSON details)
2828
- `openapi` (schemas via `utoipa`)
2929
- `sqlx`, `reqwest`, `redis`, `validator`, `config`, `tokio`, `multipart` (error conversions)
30-
- **Clean wire contract.** A small `ErrorResponse { status, message, details? }` payload for HTTP, with optional OpenAPI schema. No leaking of internal sources into the response.
30+
- **Clean wire contract.** A small `ErrorResponse { status, code, message, details?, retry?, www_authenticate? }` payload for HTTP, with optional OpenAPI schema. No leaking of internal sources.
3131
- **One log at the boundary.** Use `tracing` once when converting to HTTP, avoiding duplicate logs and keeping fields stable (`kind`, `status`, `message`).
3232
- **Less boilerplate.** Built-in `From<...>` conversions for common libs and a compact prelude for handler signatures.
3333
- **Consistent across a workspace.** Share the same error surface between services and crates, making clients and tests simpler.
3434

35+
> *Since v0.3.0: stable AppCode enum and extended ErrorResponse with retry/authentication metadata*
36+
37+
3538
---
3639

3740
## Installation
3841

3942
```toml
4043
[dependencies]
4144
# lean core, no extra deps
42-
masterror = { version = "0.2", default-features = false }
45+
masterror = { version = "0.3", default-features = false }
4346

4447
# Or with features:
4548
# JSON + Axum/Actix + common integrations
46-
# masterror = { version = "0.2", features = [
49+
# masterror = { version = "0.3", features = [
4750
# "axum", "actix", "serde_json", "openapi",
4851
# "sqlx", "reqwest", "redis", "validator", "config", "tokio"
4952
# ] }
@@ -82,14 +85,19 @@ fn do_work(flag: bool) -> AppResult<()> {
8285

8386
`ErrorResponse` is a wire-level payload for HTTP APIs. You can build it directly or convert from `AppError`:
8487

88+
8589
```rust
86-
use masterror::{AppError, AppErrorKind, ErrorResponse};
90+
use masterror::{AppError, AppErrorKind, AppCode, ErrorResponse};
8791

88-
let app_err = AppError::new(AppErrorKind::NotFound, "User not found");
89-
let resp: ErrorResponse = (&app_err).into();
90-
assert_eq!(resp.status, 404);
92+
let app_err = AppError::new(AppErrorKind::Unauthorized, "Token expired");
93+
let resp: ErrorResponse = (&app_err).into()
94+
.with_retry_after_secs(30)
95+
.with_www_authenticate(r#"Bearer realm="api", error="invalid_token""#);
96+
97+
assert_eq!(resp.status, 401);
9198
```
9299

100+
93101
---
94102

95103
## Web framework integrations
@@ -124,10 +132,12 @@ async fn err() -> AppResult<&'static str> {
124132
Err(AppError::forbidden("No access"))
125133
}
126134

135+
127136
#[get("/payload")]
128137
async fn payload() -> impl Responder {
129-
ErrorResponse::new(422, "Validation failed")
138+
ErrorResponse::new(422, AppCode::Validation, "Validation failed")
130139
}
140+
131141
```
132142

133143
---
@@ -138,7 +148,7 @@ Enable `openapi` to derive an OpenAPI schema for `ErrorResponse` (via `utoipa`).
138148

139149
```toml
140150
[dependencies]
141-
masterror = { version = "0.2", features = ["openapi", "serde_json"] }
151+
masterror = { version = "0.3", features = ["openapi", "serde_json"] }
142152
utoipa = "5"
143153
```
144154

@@ -180,13 +190,13 @@ All mappings are conservative and avoid leaking internals:
180190
Minimal core:
181191

182192
```toml
183-
masterror = { version = "0.2", default-features = false }
193+
masterror = { version = "0.3", default-features = false }
184194
```
185195

186196
API service (Axum + JSON + common deps):
187197

188198
```toml
189-
masterror = { version = "0.2", features = [
199+
masterror = { version = "0.3", features = [
190200
"axum", "serde_json", "openapi",
191201
"sqlx", "reqwest", "redis", "validator", "config", "tokio"
192202
] }
@@ -195,12 +205,23 @@ masterror = { version = "0.2", features = [
195205
API service (Actix + JSON + common deps):
196206

197207
```toml
198-
masterror = { version = "0.2", features = [
208+
masterror = { version = "0.3", features = [
199209
"actix", "serde_json", "openapi",
200210
"sqlx", "reqwest", "redis", "validator", "config", "tokio"
201211
] }
202212
```
203213

214+
---
215+
216+
## Migration from 0.2.x to 0.3.0
217+
218+
- Replace `ErrorResponse::new(status, "msg")` with
219+
`ErrorResponse::new(status, AppCode::<Variant>, "msg")`
220+
- Use `.with_retry_after_secs(...)` and `.with_www_authenticate(...)`
221+
if you want to surface HTTP headers.
222+
- `ErrorResponse::new_legacy` is provided temporarily as a deprecated shim.
223+
224+
204225
---
205226

206227
## Versioning and MSRV

0 commit comments

Comments
 (0)