1#[macro_use]
28pub mod macros;
29
30pub mod auth;
31pub mod client;
32pub mod error;
33pub mod rate_limit;
34pub mod request;
35
36const LOG_BODY_MAX_LEN: usize = 512;
38
39pub fn truncate_for_log(s: &str) -> std::borrow::Cow<'_, str> {
44 if s.len() <= LOG_BODY_MAX_LEN {
45 std::borrow::Cow::Borrowed(s)
46 } else {
47 let truncated = &s[..s.floor_char_boundary(LOG_BODY_MAX_LEN)];
48 std::borrow::Cow::Owned(format!("{}... [truncated]", truncated))
49 }
50}
51
52pub use auth::{current_timestamp, Base64Format, Signer};
53pub use client::{
54 retry_after_header, HttpClient, HttpClientBuilder, DEFAULT_POOL_SIZE, DEFAULT_TIMEOUT_MS,
55};
56pub use error::ApiError;
57pub use rate_limit::{RateLimiter, RetryConfig};
58pub use request::{QueryBuilder, Request, RequestError};
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn test_truncate_for_log_short_string_unchanged() {
66 let short = "hello world";
67 let result = truncate_for_log(short);
68 assert_eq!(result.as_ref(), short);
69 }
70
71 #[test]
72 fn test_truncate_for_log_exact_limit_unchanged() {
73 let exact = "a".repeat(LOG_BODY_MAX_LEN);
74 let result = truncate_for_log(&exact);
75 assert_eq!(result.as_ref(), exact.as_str());
76 }
77
78 #[test]
79 fn test_truncate_for_log_over_limit_truncated() {
80 let long = "x".repeat(LOG_BODY_MAX_LEN + 100);
81 let result = truncate_for_log(&long);
82 assert!(result.ends_with("... [truncated]"));
83 assert!(result.len() < long.len());
84 }
85
86 #[test]
87 fn test_truncate_for_log_multibyte_char_boundary() {
88 let mut s = "a".repeat(LOG_BODY_MAX_LEN - 1);
90 s.push('\u{1F600}'); s.push_str("overflow");
92 let result = truncate_for_log(&s);
93 assert!(result.ends_with("... [truncated]"));
94 assert!(result.is_char_boundary(0));
96 }
97}