fix: Address critical issues in client configuration registry implementation

- Fixed HeaderMapInit parsing bug that would cause compilation errors
- Added comprehensive documentation with usage examples and DSL reference
- Improved error handling with better error messages for invalid headers
- Added test coverage for both macro and registry functionality
- Added debug inspection capabilities for registered configurations
- Fixed module name conflicts in tests by using separate modules

All tests now passing:
- 7 macro tests validating DSL parsing and code generation
- 4 registry tests verifying configuration collection and application
This commit is contained in:
durch
2025-09-05 20:58:19 +02:00
parent 23d4c75597
commit 4f08161315
7 changed files with 311 additions and 28 deletions
+3 -3
View File
@@ -136,10 +136,10 @@
//! ```
#![warn(missing_docs)]
pub use inventory;
pub use reqwest;
pub use reqwest::ClientBuilder as ReqwestClientBuilder;
pub use reqwest::StatusCode;
pub use reqwest;
pub use inventory;
pub mod registry;
@@ -181,10 +181,10 @@ mod path;
pub use dns::{HickoryDnsError, HickoryDnsResolver};
// helper for generating user agent based on binary information
use crate::registry::default_builder;
#[doc(hidden)]
pub use nym_bin_common::bin_info;
use nym_http_api_client_macro::client_defaults;
use crate::registry::default_builder;
client_defaults!(
priority = -100;
+30 -4
View File
@@ -1,6 +1,14 @@
/// A record contributed by any crate describing how to mutate a reqwest::ClientBuilder.
//! Global registry for HTTP client configurations.
//!
//! This module provides a compile-time registry system that allows any crate
//! in the workspace to contribute configuration modifications to HTTP clients.
use crate::ReqwestClientBuilder;
/// A configuration record that modifies a `ReqwestClientBuilder`.
///
/// Records are collected at compile-time via the `inventory` crate and
/// applied in priority order when building HTTP clients.
pub struct ConfigRecord {
/// Lower numbers run earlier.
pub priority: i32,
@@ -10,18 +18,36 @@ pub struct ConfigRecord {
inventory::collect!(ConfigRecord);
/// Returns the default builder with all registered configurations applied.
pub fn default_builder() -> ReqwestClientBuilder {
let mut b = ReqwestClientBuilder::new();
let mut records: Vec<&'static ConfigRecord> =
inventory::iter::<ConfigRecord>
.into_iter()
.collect();
inventory::iter::<ConfigRecord>.into_iter().collect();
records.sort_by_key(|r| r.priority); // lower runs first
for r in records {
b = (r.apply)(b);
}
b
}
/// Builds a client using the default builder with all registered configurations.
pub fn build_client() -> reqwest::Result<reqwest::Client> {
default_builder().build()
}
/// Debug function to inspect registered configurations.
/// Returns a vector of (priority, function_pointer) tuples for debugging.
#[cfg(debug_assertions)]
pub fn inspect_registered_configs() -> Vec<(i32, usize)> {
let mut configs: Vec<(i32, usize)> = inventory::iter::<ConfigRecord>
.into_iter()
.map(|record| (record.priority, record.apply as usize))
.collect();
configs.sort_by_key(|(priority, _)| *priority);
configs
}
/// Returns the count of registered configuration records.
pub fn registered_config_count() -> usize {
inventory::iter::<ConfigRecord>.into_iter().count()
}
@@ -0,0 +1,70 @@
use nym_http_api_client::registry;
// Create separate modules to avoid name conflicts
mod config_early {
use nym_http_api_client_macro::client_defaults;
client_defaults!(
priority = -200;
tcp_nodelay = true
);
}
mod config_late {
use nym_http_api_client_macro::client_defaults;
client_defaults!(
priority = 100;
pool_idle_timeout = std::time::Duration::from_secs(90)
);
}
#[test]
fn test_registry_collects_configs() {
// Verify that configurations are being registered
let count = registry::registered_config_count();
// Should have at least the ones we registered above plus the default from lib.rs
assert!(
count >= 3,
"Expected at least 3 registered configs, got {}",
count
);
}
#[test]
fn test_default_builder_applies_configs() {
// Test that default_builder returns a configured builder
let _builder = registry::default_builder();
// The builder should have all configurations applied
// We can't easily inspect the internals, but we verify it doesn't panic
}
#[test]
fn test_build_client_works() {
// Test that we can successfully build a client with all configurations
let result = registry::build_client();
assert!(result.is_ok(), "Failed to build client: {:?}", result.err());
}
#[cfg(debug_assertions)]
#[test]
fn test_inspect_configs() {
// In debug mode, test that we can inspect registered configurations
let configs = registry::inspect_registered_configs();
// Verify configs are sorted by priority
for window in configs.windows(2) {
assert!(window[0].0 <= window[1].0, "Configs not sorted by priority");
}
// Verify we have configs at different priority levels
let priorities: Vec<i32> = configs.iter().map(|(p, _)| *p).collect();
assert!(
priorities.iter().any(|&p| p < 0),
"Expected negative priority configs"
);
assert!(
priorities.iter().any(|&p| p >= 0),
"Expected non-negative priority configs"
);
}