Skip to content

Commit 52e7786

Browse files
committed
chore: add validators and tests
1 parent 66145c9 commit 52e7786

File tree

5 files changed

+1755
-0
lines changed

5 files changed

+1755
-0
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/// Validates that a deserialized string field matches a given constant value.
2+
///
3+
/// This function is intended for use with `#[serde(deserialize_with)]` to enforce
4+
/// that a field in a struct always has a fixed, expected string value during deserialization.
5+
///
6+
/// # Parameters
7+
/// - `struct_name`: The name of the struct where this validation is applied.
8+
/// - `field_name`: The name of the field being validated.
9+
/// - `expected`: The expected constant string value for the field.
10+
/// - `deserializer`: The Serde deserializer for the field.
11+
///
12+
/// # Returns
13+
/// - `Ok(String)` if the deserialized value matches the expected value.
14+
/// - `Err(D::Error)` if the value differs, with an error message indicating
15+
/// which struct and field failed validation.
16+
///
17+
pub fn const_str_validator<'de, D>(
18+
struct_name: &'static str,
19+
field_name: &'static str,
20+
expected: &'static str,
21+
deserializer: D,
22+
) -> Result<String, D::Error>
23+
where
24+
D: serde::de::Deserializer<'de>,
25+
{
26+
let value: String = serde::Deserialize::deserialize(deserializer)?;
27+
if value == expected {
28+
Ok(value)
29+
} else {
30+
Err(serde::de::Error::custom(format!(
31+
"Expected field `{field_name}` in struct `{struct_name}` as const value '{expected}', but got '{value}'",
32+
)))
33+
}
34+
}
35+
36+
/// Macro to generate a field-specific validator function for use with Serde.
37+
///
38+
/// This avoids repetitive boilerplate when you have multiple fields/structs
39+
/// requiring constant string validation.
40+
///
41+
/// # Syntax
42+
/// ```ignore
43+
/// validate!(fn_name, "StructName", "field_name", "expected_value");
44+
/// ```
45+
///
46+
/// - `fn_name`: The function name to generate.
47+
/// - `StructName`: The name of the struct (for error messages).
48+
/// - `field_name`: The name of the field (for error messages).
49+
/// - `expected_value`: The required constant string value.
50+
///
51+
macro_rules! validate {
52+
($func_name:ident, $struct:expr, $field:expr, $expected:expr $(,)?) => {
53+
pub(crate) fn $func_name<'de, D>(deserializer: D) -> Result<String, D::Error>
54+
where
55+
D: serde::de::Deserializer<'de>,
56+
{
57+
const_str_validator($struct, $field, $expected, deserializer)
58+
}
59+
};
60+
}
61+
62+
//* Validator Functions *//
63+
validate!(call_tool_request_method, "CallToolRequest", "method", "tools/call");
64+
validate!(
65+
cancelled_notification_method,
66+
"CancelledNotification",
67+
"method",
68+
"notifications/cancelled"
69+
);
70+
validate!(complete_request_method, "CompleteRequest", "method", "completion/complete");
71+
validate!(
72+
create_message_request_method,
73+
"CreateMessageRequest",
74+
"method",
75+
"sampling/createMessage"
76+
);
77+
validate!(embedded_resource_type_, "EmbeddedResource", "type_", "resource");
78+
validate!(get_prompt_request_method, "GetPromptRequest", "method", "prompts/get");
79+
validate!(image_content_type_, "ImageContent", "type_", "image");
80+
validate!(initialize_request_method, "InitializeRequest", "method", "initialize");
81+
validate!(
82+
initialized_notification_method,
83+
"InitializedNotification",
84+
"method",
85+
"notifications/initialized"
86+
);
87+
validate!(jsonrpc_error_jsonrpc, "JsonrpcError", "jsonrpc", "2.0");
88+
validate!(jsonrpc_notification_jsonrpc, "JsonrpcNotification", "jsonrpc", "2.0");
89+
validate!(jsonrpc_request_jsonrpc, "JsonrpcRequest", "jsonrpc", "2.0");
90+
validate!(jsonrpc_response_jsonrpc, "JsonrpcResponse", "jsonrpc", "2.0");
91+
validate!(list_prompts_request_method, "ListPromptsRequest", "method", "prompts/list");
92+
validate!(
93+
list_resource_templates_request_method,
94+
"ListResourceTemplatesRequest",
95+
"method",
96+
"resources/templates/list"
97+
);
98+
validate!(
99+
list_resources_request_method,
100+
"ListResourcesRequest",
101+
"method",
102+
"resources/list"
103+
);
104+
validate!(list_roots_request_method, "ListRootsRequest", "method", "roots/list");
105+
validate!(list_tools_request_method, "ListToolsRequest", "method", "tools/list");
106+
validate!(
107+
logging_message_notification_method,
108+
"LoggingMessageNotification",
109+
"method",
110+
"notifications/message"
111+
);
112+
validate!(ping_request_method, "PingRequest", "method", "ping");
113+
validate!(
114+
progress_notification_method,
115+
"ProgressNotification",
116+
"method",
117+
"notifications/progress"
118+
);
119+
validate!(
120+
prompt_list_changed_notification_method,
121+
"PromptListChangedNotification",
122+
"method",
123+
"notifications/prompts/list_changed"
124+
);
125+
validate!(prompt_reference_type_, "PromptReference", "type_", "ref/prompt");
126+
validate!(
127+
read_resource_request_method,
128+
"ReadResourceRequest",
129+
"method",
130+
"resources/read"
131+
);
132+
validate!(
133+
resource_list_changed_notification_method,
134+
"ResourceListChangedNotification",
135+
"method",
136+
"notifications/resources/list_changed"
137+
);
138+
validate!(resource_reference_type_, "ResourceReference", "type_", "ref/resource");
139+
validate!(
140+
resource_updated_notification_method,
141+
"ResourceUpdatedNotification",
142+
"method",
143+
"notifications/resources/updated"
144+
);
145+
validate!(
146+
roots_list_changed_notification_method,
147+
"RootsListChangedNotification",
148+
"method",
149+
"notifications/roots/list_changed"
150+
);
151+
validate!(set_level_request_method, "SetLevelRequest", "method", "logging/setLevel");
152+
validate!(subscribe_request_method, "SubscribeRequest", "method", "resources/subscribe");
153+
validate!(text_content_type_, "TextContent", "type_", "text");
154+
validate!(tool_input_schema_type_, "ToolInputSchema", "type_", "object");
155+
validate!(
156+
tool_list_changed_notification_method,
157+
"ToolListChangedNotification",
158+
"method",
159+
"notifications/tools/list_changed"
160+
);
161+
validate!(
162+
unsubscribe_request_method,
163+
"UnsubscribeRequest",
164+
"method",
165+
"resources/unsubscribe"
166+
);
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/// Validates that a deserialized string field matches a given constant value.
2+
///
3+
/// This function is intended for use with `#[serde(deserialize_with)]` to enforce
4+
/// that a field in a struct always has a fixed, expected string value during deserialization.
5+
///
6+
/// # Parameters
7+
/// - `struct_name`: The name of the struct where this validation is applied.
8+
/// - `field_name`: The name of the field being validated.
9+
/// - `expected`: The expected constant string value for the field.
10+
/// - `deserializer`: The Serde deserializer for the field.
11+
///
12+
/// # Returns
13+
/// - `Ok(String)` if the deserialized value matches the expected value.
14+
/// - `Err(D::Error)` if the value differs, with an error message indicating
15+
/// which struct and field failed validation.
16+
///
17+
pub fn const_str_validator<'de, D>(
18+
struct_name: &'static str,
19+
field_name: &'static str,
20+
expected: &'static str,
21+
deserializer: D,
22+
) -> Result<String, D::Error>
23+
where
24+
D: serde::de::Deserializer<'de>,
25+
{
26+
let value: String = serde::Deserialize::deserialize(deserializer)?;
27+
if value == expected {
28+
Ok(value)
29+
} else {
30+
Err(serde::de::Error::custom(format!(
31+
"Expected field `{field_name}` in struct `{struct_name}` as const value '{expected}', but got '{value}'",
32+
)))
33+
}
34+
}
35+
36+
/// Macro to generate a field-specific validator function for use with Serde.
37+
///
38+
/// This avoids repetitive boilerplate when you have multiple fields/structs
39+
/// requiring constant string validation.
40+
///
41+
/// # Syntax
42+
/// ```ignore
43+
/// validate!(fn_name, "StructName", "field_name", "expected_value");
44+
/// ```
45+
///
46+
/// - `fn_name`: The function name to generate.
47+
/// - `StructName`: The name of the struct (for error messages).
48+
/// - `field_name`: The name of the field (for error messages).
49+
/// - `expected_value`: The required constant string value.
50+
///
51+
macro_rules! validate {
52+
($func_name:ident, $struct:expr, $field:expr, $expected:expr $(,)?) => {
53+
pub(crate) fn $func_name<'de, D>(deserializer: D) -> Result<String, D::Error>
54+
where
55+
D: serde::de::Deserializer<'de>,
56+
{
57+
const_str_validator($struct, $field, $expected, deserializer)
58+
}
59+
};
60+
}
61+
62+
//* Validator Functions *//
63+
validate!(audio_content_type_, "AudioContent", "type_", "audio");
64+
validate!(call_tool_request_method, "CallToolRequest", "method", "tools/call");
65+
validate!(
66+
cancelled_notification_method,
67+
"CancelledNotification",
68+
"method",
69+
"notifications/cancelled"
70+
);
71+
validate!(complete_request_method, "CompleteRequest", "method", "completion/complete");
72+
validate!(
73+
create_message_request_method,
74+
"CreateMessageRequest",
75+
"method",
76+
"sampling/createMessage"
77+
);
78+
validate!(embedded_resource_type_, "EmbeddedResource", "type_", "resource");
79+
validate!(get_prompt_request_method, "GetPromptRequest", "method", "prompts/get");
80+
validate!(image_content_type_, "ImageContent", "type_", "image");
81+
validate!(initialize_request_method, "InitializeRequest", "method", "initialize");
82+
validate!(
83+
initialized_notification_method,
84+
"InitializedNotification",
85+
"method",
86+
"notifications/initialized"
87+
);
88+
validate!(jsonrpc_error_jsonrpc, "JsonrpcError", "jsonrpc", "2.0");
89+
validate!(jsonrpc_notification_jsonrpc, "JsonrpcNotification", "jsonrpc", "2.0");
90+
validate!(jsonrpc_request_jsonrpc, "JsonrpcRequest", "jsonrpc", "2.0");
91+
validate!(jsonrpc_response_jsonrpc, "JsonrpcResponse", "jsonrpc", "2.0");
92+
validate!(list_prompts_request_method, "ListPromptsRequest", "method", "prompts/list");
93+
validate!(
94+
list_resource_templates_request_method,
95+
"ListResourceTemplatesRequest",
96+
"method",
97+
"resources/templates/list"
98+
);
99+
validate!(
100+
list_resources_request_method,
101+
"ListResourcesRequest",
102+
"method",
103+
"resources/list"
104+
);
105+
validate!(list_roots_request_method, "ListRootsRequest", "method", "roots/list");
106+
validate!(list_tools_request_method, "ListToolsRequest", "method", "tools/list");
107+
validate!(
108+
logging_message_notification_method,
109+
"LoggingMessageNotification",
110+
"method",
111+
"notifications/message"
112+
);
113+
validate!(ping_request_method, "PingRequest", "method", "ping");
114+
validate!(
115+
progress_notification_method,
116+
"ProgressNotification",
117+
"method",
118+
"notifications/progress"
119+
);
120+
validate!(
121+
prompt_list_changed_notification_method,
122+
"PromptListChangedNotification",
123+
"method",
124+
"notifications/prompts/list_changed"
125+
);
126+
validate!(prompt_reference_type_, "PromptReference", "type_", "ref/prompt");
127+
validate!(
128+
read_resource_request_method,
129+
"ReadResourceRequest",
130+
"method",
131+
"resources/read"
132+
);
133+
validate!(
134+
resource_list_changed_notification_method,
135+
"ResourceListChangedNotification",
136+
"method",
137+
"notifications/resources/list_changed"
138+
);
139+
validate!(resource_reference_type_, "ResourceReference", "type_", "ref/resource");
140+
validate!(
141+
resource_updated_notification_method,
142+
"ResourceUpdatedNotification",
143+
"method",
144+
"notifications/resources/updated"
145+
);
146+
validate!(
147+
roots_list_changed_notification_method,
148+
"RootsListChangedNotification",
149+
"method",
150+
"notifications/roots/list_changed"
151+
);
152+
validate!(set_level_request_method, "SetLevelRequest", "method", "logging/setLevel");
153+
validate!(subscribe_request_method, "SubscribeRequest", "method", "resources/subscribe");
154+
validate!(text_content_type_, "TextContent", "type_", "text");
155+
validate!(tool_input_schema_type_, "ToolInputSchema", "type_", "object");
156+
validate!(
157+
tool_list_changed_notification_method,
158+
"ToolListChangedNotification",
159+
"method",
160+
"notifications/tools/list_changed"
161+
);
162+
validate!(
163+
unsubscribe_request_method,
164+
"UnsubscribeRequest",
165+
"method",
166+
"resources/unsubscribe"
167+
);

0 commit comments

Comments
 (0)