From 302fcb72fcf2ff82e7d022198555ae1db857ca1c Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Sat, 20 Jun 2026 11:21:54 +0300 Subject: [PATCH 1/2] fix(config): mark config structs non_exhaustive The public config structs are loaded from YAML via Deserialize and are not meant to be constructed with struct literals by downstream crates. Marking every config struct (and DescriptorSource) #[non_exhaustive] stops field additions/removals from being breaking changes, so the config can evolve without forcing a major bump each time and without tripping cargo-semver-checks on every new field. BREAKING CHANGE: config structs are now #[non_exhaustive]; downstream crates can no longer build them with struct literals or match them exhaustively. Construct config via ProxyConfig::from_file / Deserialize. Closes #43 --- src/config.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/config.rs b/src/config.rs index 381dc78..c60ab81 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; /// Top-level proxy configuration (loaded from YAML). #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct ProxyConfig { /// Upstream gRPC service(s). pub upstream: UpstreamConfig, @@ -81,6 +82,7 @@ fn default_forwarded_headers() -> Vec { /// Upstream gRPC service configuration. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct UpstreamConfig { /// gRPC upstream address (e.g., "http://localhost:4180"). pub default: String, @@ -88,6 +90,7 @@ pub struct UpstreamConfig { /// Descriptor loading source. #[derive(Debug, Clone)] +#[non_exhaustive] pub enum DescriptorSource { /// Pre-compiled descriptor file. File { file: PathBuf }, @@ -128,6 +131,7 @@ where /// Listen address configuration. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct ListenConfig { /// HTTP listen address (default: "0.0.0.0:8080"). #[serde(default = "default_http_listen")] @@ -148,6 +152,7 @@ impl Default for ListenConfig { /// Service identity. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct ServiceConfig { /// Service name (appears in /health response and metrics namespace). #[serde(default = "default_service_name")] @@ -168,6 +173,7 @@ impl Default for ServiceConfig { /// Path alias (rewrite before routing). #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct AliasConfig { pub from: String, pub to: String, @@ -175,6 +181,7 @@ pub struct AliasConfig { /// OpenAPI generation config. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct OpenApiConfig { #[serde(default = "default_true")] pub enabled: bool, @@ -204,6 +211,7 @@ fn default_true() -> bool { /// Auth configuration. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct AuthConfig { /// Auth mode: "none", "jwt", "api_key". #[serde(default = "default_auth_mode")] @@ -228,6 +236,7 @@ fn default_auth_mode() -> String { /// JWT validation config. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct JwtConfig { /// JWKS URI for key discovery. #[serde(default)] @@ -256,6 +265,7 @@ fn default_roles_claim() -> String { /// Forward auth config. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct ForwardAuthConfig { #[serde(default)] pub enabled: bool, @@ -278,6 +288,7 @@ fn default_forward_auth_path() -> String { /// Route policy entry. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct RoutePolicyConfig { pub path: String, #[serde(default = "default_methods_all")] @@ -296,6 +307,7 @@ fn default_methods_all() -> Vec { /// (`envoy.service.auth.v3.Authorization/Check`). Interops with OPA and any /// ext_authz server. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct AuthzConfig { /// Enable external authorization for proxied API requests. #[serde(default)] @@ -319,6 +331,7 @@ fn default_authz_timeout_ms() -> u64 { /// Shield (rate limiting) configuration. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct ShieldConfig { #[serde(default)] pub enabled: bool, @@ -352,6 +365,7 @@ fn default_window_secs() -> u64 { /// Endpoint classification for rate limiting. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct EndpointClassConfig { /// Glob pattern (e.g., "/v1/auth/**"). pub pattern: String, @@ -363,6 +377,7 @@ pub struct EndpointClassConfig { /// Per-identifier rate limiting config. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct IdentifierEndpointConfig { pub path: String, pub body_field: String, @@ -371,6 +386,7 @@ pub struct IdentifierEndpointConfig { /// OIDC discovery config. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct OidcDiscoveryConfig { #[serde(default)] pub enabled: bool, @@ -389,6 +405,7 @@ pub struct OidcDiscoveryConfig { /// Signing key config for JWKS endpoint. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct SigningKeyConfig { #[serde(default = "default_algorithm")] pub algorithm: String, @@ -401,6 +418,7 @@ fn default_algorithm() -> String { /// Maintenance mode config. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct MaintenanceConfig { #[serde(default)] pub enabled: bool, @@ -436,6 +454,7 @@ impl Default for MaintenanceConfig { /// CORS configuration. #[derive(Debug, Clone, Default, Deserialize)] +#[non_exhaustive] pub struct CorsConfig { /// Allowed origins. Empty = permissive (dev mode). #[serde(default)] @@ -444,6 +463,7 @@ pub struct CorsConfig { /// Logging configuration. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct LoggingConfig { #[serde(default = "default_log_level")] pub level: String, @@ -469,6 +489,7 @@ impl Default for LoggingConfig { /// Metrics endpoint classification. #[derive(Debug, Clone, Deserialize)] +#[non_exhaustive] pub struct MetricsClassConfig { /// Glob pattern for path matching. pub pattern: String, From b88ac13fcbd8cf46b823eca2cd39b7687a756a2b Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Sat, 20 Jun 2026 11:22:06 +0300 Subject: [PATCH 2/2] docs: center the Support the Project section Wrap the heading + QR in the centered block and use an HTML img so the donation section renders centered on GitHub / crates.io. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 069c4b5..1e38706 100644 --- a/README.md +++ b/README.md @@ -210,11 +210,11 @@ Client (HTTP/JSON) Upstream Service ``` -## Support the Project -
-![USDT TRC-20 Donation QR Code](./assets/usdt-qr.svg) +## Support the Project + +USDT TRC-20 Donation QR Code USDT (TRC-20): `TFDsezHa1cBkoeZT5q2T49Wp66K8t2DmdA`