Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 43 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ jobs:
steps:
- uses: actions/checkout@v4

# Install a minimal Rust toolchain so that `cargo metadata` is available.
- name: Install Rust (stable, minimal)
uses: dtolnay/rust-toolchain@v1
with:
Expand Down Expand Up @@ -50,7 +49,6 @@ jobs:
echo "rust-version is not set for crate: ${CRATE_NAME}"
exit 1
fi
# Normalize X.Y to X.Y.0
if [[ "$RV" =~ ^[0-9]+\.[0-9]+$ ]]; then
RV="${RV}.0"
fi
Expand Down Expand Up @@ -84,8 +82,49 @@ jobs:
with:
toolchain: ${{ steps.msrv.outputs.msrv }}

- name: Publish to crates.io
- name: Maybe publish masterror-derive
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo +${{ steps.msrv.outputs.msrv }} publish --locked --token "$CARGO_REGISTRY_TOKEN"
shell: bash
run: |
set -euo pipefail
if cargo metadata --no-deps --format-version=1 \
| jq -e '.packages[] | select(.name=="masterror-derive" and .source==null)' >/dev/null; then
echo "Publishing masterror-derive..."
n=0
until [ $n -ge 5 ]
do
if cargo +${{ steps.msrv.outputs.msrv }} publish -p masterror-derive --locked --token "$CARGO_REGISTRY_TOKEN"; then
echo "masterror-derive published."
break
fi
n=$((n+1))
echo "Retry $n/5 for masterror-derive..."
sleep $((5*n))
done
else
echo "No local masterror-derive found; skipping."
fi

- name: Wait for crates.io index sync
run: sleep 15

- name: Publish masterror
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
shell: bash
run: |
set -euo pipefail
n=0
until [ $n -ge 5 ]
do
if cargo +${{ steps.msrv.outputs.msrv }} publish -p masterror --locked --token "$CARGO_REGISTRY_TOKEN"; then
echo "masterror published."
exit 0
fi
n=$((n+1))
echo "Retry $n/5 for masterror..."
sleep $((10*n))
done
echo "Failed to publish masterror after retries."
exit 1
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

## [0.10.5] - 2025-09-20

### Added
- Re-exported `masterror-derive` macros from `masterror` so consumers only depend on a single crate while deriving application errors.

### Changed
- Published `masterror-derive` as a standalone crate (`0.6.1`) and configured the release workflow to publish it before `masterror` with retries and tag/MSRV validation.

### Documentation
- Described `#[provide]` telemetry providers and `#[app_error]` conversions with
end-to-end examples in the derive guide ([README](README.md#structured-telemetry-providers-and-apperror-mappings),
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "masterror"
version = "0.10.4"
version = "0.10.5"
rust-version = "1.90"
edition = "2024"
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -71,11 +71,11 @@ turnkey = []
openapi = ["dep:utoipa"]

[workspace.dependencies]
masterror-derive = { version = "0.6.0" }
masterror-derive = { version = "0.6.1" }
masterror-template = { version = "0.3.1" }

[dependencies]
masterror-derive = { workspace = true }
masterror-derive = { version = "0.6" }
masterror-template = { workspace = true }
tracing = "0.1"

Expand Down
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ Stable categories, conservative HTTP mapping, no `unsafe`.

~~~toml
[dependencies]
masterror = { version = "0.10.4", default-features = false }
masterror = { version = "0.10.5", default-features = false }
# or with features:
# masterror = { version = "0.10.4", features = [
# masterror = { version = "0.10.5", features = [
# "axum", "actix", "openapi", "serde_json",
# "sqlx", "sqlx-migrate", "reqwest", "redis",
# "validator", "config", "tokio", "multipart",
Expand Down Expand Up @@ -66,10 +66,10 @@ masterror = { version = "0.10.4", default-features = false }
~~~toml
[dependencies]
# lean core
masterror = { version = "0.10.4", default-features = false }
masterror = { version = "0.10.5", default-features = false }

# with Axum/Actix + JSON + integrations
# masterror = { version = "0.10.4", features = [
# masterror = { version = "0.10.5", features = [
# "axum", "actix", "openapi", "serde_json",
# "sqlx", "sqlx-migrate", "reqwest", "redis",
# "validator", "config", "tokio", "multipart",
Expand Down Expand Up @@ -623,13 +623,13 @@ assert_eq!(resp.status, 401);
Minimal core:

~~~toml
masterror = { version = "0.10.4", default-features = false }
masterror = { version = "0.10.5", default-features = false }
~~~

API (Axum + JSON + deps):

~~~toml
masterror = { version = "0.10.4", features = [
masterror = { version = "0.10.5", features = [
"axum", "serde_json", "openapi",
"sqlx", "reqwest", "redis", "validator", "config", "tokio"
] }
Expand All @@ -638,7 +638,7 @@ masterror = { version = "0.10.4", features = [
API (Actix + JSON + deps):

~~~toml
masterror = { version = "0.10.4", features = [
masterror = { version = "0.10.5", features = [
"actix", "serde_json", "openapi",
"sqlx", "reqwest", "redis", "validator", "config", "tokio"
] }
Expand Down Expand Up @@ -709,4 +709,3 @@ MSRV = 1.90 (may raise in minor, never in patch).
Apache-2.0 OR MIT, at your option.

</details>

3 changes: 1 addition & 2 deletions masterror-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
[package]
name = "masterror-derive"
rust-version = "1.90"
version = "0.6.0"
version = "0.6.1"
edition = "2024"
license = "MIT OR Apache-2.0"
repository = "https://github.com/RAprogramm/masterror"
readme = "README.md"
description = "Derive macros for masterror"
publish = false

[lib]
proc-macro = true
Expand Down
65 changes: 18 additions & 47 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,60 +206,31 @@ pub mod prelude;
pub use app_error::{AppError, AppResult};
pub use code::AppCode;
pub use kind::AppErrorKind;
/// Native derive macro for error enums and structs.
/// Re-export derive macros so users only depend on [`masterror`].
///
/// Supports `#[from]` conversions, transparent wrappers, and precise
/// diagnostics for `#[error("...")]` templates with field-aware validation.
/// # Examples
///
/// ```
/// use std::error::Error as StdError;
///
/// use masterror::Error;
/// use masterror::{AppCode, AppError, AppErrorKind, Error};
///
/// #[derive(Debug, Error)]
/// #[error("{code}: {message}")]
/// struct MiniError {
/// code: u16,
/// message: &'static str
/// #[error("missing flag: {name}")]
/// #[app_error(kind = AppErrorKind::BadRequest, code = AppCode::BadRequest, message)]
/// struct MissingFlag {
/// name: &'static str
/// }
///
/// #[derive(Debug, Error)]
/// #[error("wrapper -> {0}")]
/// struct MiniWrapper(
/// #[from]
/// #[source]
/// MiniError
/// );
///
/// #[derive(Debug, Error)]
/// #[error(transparent)]
/// struct MiniTransparent(#[from] MiniError);
///
/// let wrapped = MiniWrapper::from(MiniError {
/// code: 500,
/// message: "boom"
/// });
/// assert_eq!(wrapped.to_string(), "wrapper -> 500: boom");
/// assert_eq!(
/// StdError::source(&wrapped).map(|err| err.to_string()),
/// Some(String::from("500: boom"))
/// );
///
/// let expected_source = StdError::source(&MiniError {
/// code: 503,
/// message: "oops"
/// })
/// .map(|err| err.to_string());
/// let app: AppError = MissingFlag {
/// name: "feature"
/// }
/// .into();
/// assert!(matches!(app.kind, AppErrorKind::BadRequest));
///
/// let transparent = MiniTransparent::from(MiniError {
/// code: 503,
/// message: "oops"
/// });
/// assert_eq!(transparent.to_string(), "503: oops");
/// assert_eq!(
/// StdError::source(&transparent).map(|err| err.to_string()),
/// expected_source
/// );
/// let code: AppCode = MissingFlag {
/// name: "other"
/// }
/// .into();
/// assert!(matches!(code, AppCode::BadRequest));
/// ```
pub use masterror_derive::Error;
pub use masterror_derive::*;
pub use response::{ErrorResponse, RetryAdvice};
Loading