Skip to content

Commit 952bb5f

Browse files
committed
feat(transport): add constructors for non_exhaustive error types
AuthRequiredError, InsufficientScopeError, and DynamicTransportError were marked #[non_exhaustive] in #715/#768 but don't have constructors usable by external crates. Add new() for the error types and from_parts() for DynamicTransportError (the existing new() requires a Transport type parameter, making it unusable for test fixtures). Fixes #805
1 parent a64be23 commit 952bb5f

3 files changed

Lines changed: 51 additions & 9 deletions

File tree

crates/rmcp/src/transport.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,24 @@ impl DynamicTransportError {
252252
error: Box::new(e),
253253
}
254254
}
255+
256+
/// Create a `DynamicTransportError` from raw parts.
257+
///
258+
/// Unlike [`new`](Self::new), this does not require a concrete [`Transport`] type,
259+
/// making it usable in test fixtures and other contexts where a real transport
260+
/// implementation is not available.
261+
pub fn from_parts(
262+
transport_name: impl Into<Cow<'static, str>>,
263+
transport_type_id: std::any::TypeId,
264+
error: Box<dyn std::error::Error + Send + Sync>,
265+
) -> Self {
266+
Self {
267+
transport_name: transport_name.into(),
268+
transport_type_id,
269+
error,
270+
}
271+
}
272+
255273
pub fn downcast<T: Transport<R> + 'static, R: ServiceRole>(self) -> Result<T::Error, Self> {
256274
if !self.is::<T, R>() {
257275
Err(self)

crates/rmcp/src/transport/common/reqwest/streamable_http_client.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -284,21 +284,28 @@ impl StreamableHttpClientTransport<reqwest::Client> {
284284
#[cfg(test)]
285285
mod tests {
286286
use super::parse_json_rpc_error;
287-
use crate::{model::JsonRpcMessage, transport::streamable_http_client::InsufficientScopeError};
287+
use crate::{
288+
model::JsonRpcMessage,
289+
transport::streamable_http_client::{AuthRequiredError, InsufficientScopeError},
290+
};
291+
292+
#[test]
293+
fn auth_required_error_new() {
294+
let err = AuthRequiredError::new("Bearer realm=\"test\"".to_string());
295+
assert_eq!(err.www_authenticate_header, "Bearer realm=\"test\"");
296+
}
288297

289298
#[test]
290299
fn insufficient_scope_error_can_upgrade() {
291-
let with_scope = InsufficientScopeError {
292-
www_authenticate_header: "Bearer scope=\"admin\"".to_string(),
293-
required_scope: Some("admin".to_string()),
294-
};
300+
let with_scope = InsufficientScopeError::new(
301+
"Bearer scope=\"admin\"".to_string(),
302+
Some("admin".to_string()),
303+
);
295304
assert!(with_scope.can_upgrade());
296305
assert_eq!(with_scope.get_required_scope(), Some("admin"));
297306

298-
let without_scope = InsufficientScopeError {
299-
www_authenticate_header: "Bearer error=\"insufficient_scope\"".to_string(),
300-
required_scope: None,
301-
};
307+
let without_scope =
308+
InsufficientScopeError::new("Bearer error=\"insufficient_scope\"".to_string(), None);
302309
assert!(!without_scope.can_upgrade());
303310
assert_eq!(without_scope.get_required_scope(), None);
304311
}

crates/rmcp/src/transport/streamable_http_client.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ pub struct AuthRequiredError {
2929
pub www_authenticate_header: String,
3030
}
3131

32+
impl AuthRequiredError {
33+
/// Create a new `AuthRequiredError` instance.
34+
pub fn new(www_authenticate_header: String) -> Self {
35+
Self {
36+
www_authenticate_header,
37+
}
38+
}
39+
}
40+
3241
#[derive(Debug)]
3342
#[non_exhaustive]
3443
pub struct InsufficientScopeError {
@@ -37,6 +46,14 @@ pub struct InsufficientScopeError {
3746
}
3847

3948
impl InsufficientScopeError {
49+
/// Create a new `InsufficientScopeError` instance.
50+
pub fn new(www_authenticate_header: String, required_scope: Option<String>) -> Self {
51+
Self {
52+
www_authenticate_header,
53+
required_scope,
54+
}
55+
}
56+
4057
/// check if scope upgrade is possible (i.e., we know what scope is required)
4158
pub fn can_upgrade(&self) -> bool {
4259
self.required_scope.is_some()

0 commit comments

Comments
 (0)