1#![allow(missing_docs)]
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::path::PathBuf;
10use std::sync::Arc;
11use async_trait::async_trait;
12use std::io::Write;
13use tokio::sync::Mutex;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub enum PermissionMode {
19 Default,
21 AcceptEdits,
23 Plan,
25 BypassPermissions,
27}
28
29impl Default for PermissionMode {
30 fn default() -> Self {
31 Self::Default
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum ControlProtocolFormat {
38 Legacy,
40 Control,
42 Auto,
44}
45
46impl Default for ControlProtocolFormat {
47 fn default() -> Self {
48 Self::Legacy
50 }
51}
52
53#[derive(Clone)]
55pub enum McpServerConfig {
56 Stdio {
58 command: String,
60 args: Option<Vec<String>>,
62 env: Option<HashMap<String, String>>,
64 },
65 Sse {
67 url: String,
69 headers: Option<HashMap<String, String>>,
71 },
72 Http {
74 url: String,
76 headers: Option<HashMap<String, String>>,
78 },
79 Sdk {
81 name: String,
83 instance: Arc<dyn std::any::Any + Send + Sync>,
85 },
86}
87
88impl std::fmt::Debug for McpServerConfig {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 match self {
91 Self::Stdio { command, args, env } => f
92 .debug_struct("Stdio")
93 .field("command", command)
94 .field("args", args)
95 .field("env", env)
96 .finish(),
97 Self::Sse { url, headers } => f
98 .debug_struct("Sse")
99 .field("url", url)
100 .field("headers", headers)
101 .finish(),
102 Self::Http { url, headers } => f
103 .debug_struct("Http")
104 .field("url", url)
105 .field("headers", headers)
106 .finish(),
107 Self::Sdk { name, .. } => f
108 .debug_struct("Sdk")
109 .field("name", name)
110 .field("instance", &"<Arc<dyn Any>>")
111 .finish(),
112 }
113 }
114}
115
116impl Serialize for McpServerConfig {
117 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
118 where
119 S: serde::Serializer,
120 {
121 use serde::ser::SerializeMap;
122 let mut map = serializer.serialize_map(None)?;
123
124 match self {
125 Self::Stdio { command, args, env } => {
126 map.serialize_entry("type", "stdio")?;
127 map.serialize_entry("command", command)?;
128 if let Some(args) = args {
129 map.serialize_entry("args", args)?;
130 }
131 if let Some(env) = env {
132 map.serialize_entry("env", env)?;
133 }
134 }
135 Self::Sse { url, headers } => {
136 map.serialize_entry("type", "sse")?;
137 map.serialize_entry("url", url)?;
138 if let Some(headers) = headers {
139 map.serialize_entry("headers", headers)?;
140 }
141 }
142 Self::Http { url, headers } => {
143 map.serialize_entry("type", "http")?;
144 map.serialize_entry("url", url)?;
145 if let Some(headers) = headers {
146 map.serialize_entry("headers", headers)?;
147 }
148 }
149 Self::Sdk { name, .. } => {
150 map.serialize_entry("type", "sdk")?;
151 map.serialize_entry("name", name)?;
152 }
153 }
154
155 map.end()
156 }
157}
158
159impl<'de> Deserialize<'de> for McpServerConfig {
160 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
161 where
162 D: serde::Deserializer<'de>,
163 {
164 #[derive(Deserialize)]
165 #[serde(tag = "type", rename_all = "lowercase")]
166 enum McpServerConfigHelper {
167 Stdio {
168 command: String,
169 #[serde(skip_serializing_if = "Option::is_none")]
170 args: Option<Vec<String>>,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 env: Option<HashMap<String, String>>,
173 },
174 Sse {
175 url: String,
176 #[serde(skip_serializing_if = "Option::is_none")]
177 headers: Option<HashMap<String, String>>,
178 },
179 Http {
180 url: String,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 headers: Option<HashMap<String, String>>,
183 },
184 }
185
186 let helper = McpServerConfigHelper::deserialize(deserializer)?;
187 Ok(match helper {
188 McpServerConfigHelper::Stdio { command, args, env } => {
189 McpServerConfig::Stdio { command, args, env }
190 }
191 McpServerConfigHelper::Sse { url, headers } => {
192 McpServerConfig::Sse { url, headers }
193 }
194 McpServerConfigHelper::Http { url, headers } => {
195 McpServerConfig::Http { url, headers }
196 }
197 })
198 }
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub enum PermissionUpdateDestination {
205 UserSettings,
207 ProjectSettings,
209 LocalSettings,
211 Session,
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
217#[serde(rename_all = "camelCase")]
218pub enum PermissionBehavior {
219 Allow,
221 Deny,
223 Ask,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct PermissionRuleValue {
230 pub tool_name: String,
232 pub rule_content: Option<String>,
234}
235
236#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
238#[serde(rename_all = "camelCase")]
239pub enum PermissionUpdateType {
240 AddRules,
242 ReplaceRules,
244 RemoveRules,
246 SetMode,
248 AddDirectories,
250 RemoveDirectories,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256#[serde(rename_all = "camelCase")]
257pub struct PermissionUpdate {
258 #[serde(rename = "type")]
260 pub update_type: PermissionUpdateType,
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub rules: Option<Vec<PermissionRuleValue>>,
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub behavior: Option<PermissionBehavior>,
267 #[serde(skip_serializing_if = "Option::is_none")]
269 pub mode: Option<PermissionMode>,
270 #[serde(skip_serializing_if = "Option::is_none")]
272 pub directories: Option<Vec<String>>,
273 #[serde(skip_serializing_if = "Option::is_none")]
275 pub destination: Option<PermissionUpdateDestination>,
276}
277
278#[derive(Debug, Clone)]
280pub struct ToolPermissionContext {
281 pub signal: Option<Arc<dyn std::any::Any + Send + Sync>>,
283 pub suggestions: Vec<PermissionUpdate>,
285}
286
287#[derive(Debug, Clone)]
289pub struct PermissionResultAllow {
290 pub updated_input: Option<serde_json::Value>,
292 pub updated_permissions: Option<Vec<PermissionUpdate>>,
294}
295
296#[derive(Debug, Clone)]
298pub struct PermissionResultDeny {
299 pub message: String,
301 pub interrupt: bool,
303}
304
305#[derive(Debug, Clone)]
307pub enum PermissionResult {
308 Allow(PermissionResultAllow),
310 Deny(PermissionResultDeny),
312}
313
314#[async_trait]
316pub trait CanUseTool: Send + Sync {
317 async fn can_use_tool(
319 &self,
320 tool_name: &str,
321 input: &serde_json::Value,
322 context: &ToolPermissionContext,
323 ) -> PermissionResult;
324}
325
326#[derive(Debug, Clone)]
328pub struct HookContext {
329 pub signal: Option<Arc<dyn std::any::Any + Send + Sync>>,
331}
332
333#[async_trait]
335pub trait HookCallback: Send + Sync {
336 async fn execute(
338 &self,
339 input: &serde_json::Value,
340 tool_use_id: Option<&str>,
341 context: &HookContext,
342 ) -> serde_json::Value;
343}
344
345#[derive(Clone)]
347pub struct HookMatcher {
348 pub matcher: Option<serde_json::Value>,
350 pub hooks: Vec<Arc<dyn HookCallback>>,
352}
353
354#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
356#[serde(rename_all = "lowercase")]
357pub enum SettingSource {
358 User,
360 Project,
362 Local,
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct AgentDefinition {
369 pub description: String,
371 pub prompt: String,
373 #[serde(skip_serializing_if = "Option::is_none")]
375 pub tools: Option<Vec<String>>,
376 #[serde(skip_serializing_if = "Option::is_none")]
378 pub model: Option<String>,
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize)]
383#[serde(untagged)]
384pub enum SystemPrompt {
385 String(String),
387 Preset {
389 #[serde(rename = "type")]
390 preset_type: String, preset: String, #[serde(skip_serializing_if = "Option::is_none")]
393 append: Option<String>,
394 },
395}
396
397#[derive(Clone, Default)]
399pub struct ClaudeCodeOptions {
400 pub system_prompt_v2: Option<SystemPrompt>,
404 #[deprecated(since = "0.1.12", note = "Use system_prompt_v2 instead")]
407 pub system_prompt: Option<String>,
408 #[deprecated(since = "0.1.12", note = "Use system_prompt_v2 instead")]
411 pub append_system_prompt: Option<String>,
412 pub allowed_tools: Vec<String>,
414 pub disallowed_tools: Vec<String>,
416 pub permission_mode: PermissionMode,
418 pub mcp_servers: HashMap<String, McpServerConfig>,
420 pub mcp_tools: Vec<String>,
422 pub max_turns: Option<i32>,
424 pub max_thinking_tokens: i32,
426 pub max_output_tokens: Option<u32>,
428 pub model: Option<String>,
430 pub cwd: Option<PathBuf>,
432 pub continue_conversation: bool,
434 pub resume: Option<String>,
436 pub permission_prompt_tool_name: Option<String>,
438 pub settings: Option<String>,
440 pub add_dirs: Vec<PathBuf>,
442 pub extra_args: HashMap<String, Option<String>>,
444 pub env: HashMap<String, String>,
446 pub debug_stderr: Option<Arc<Mutex<dyn Write + Send + Sync>>>,
448 pub include_partial_messages: bool,
450 pub can_use_tool: Option<Arc<dyn CanUseTool>>,
452 pub hooks: Option<HashMap<String, Vec<HookMatcher>>>,
454 pub control_protocol_format: ControlProtocolFormat,
456
457 pub setting_sources: Option<Vec<SettingSource>>,
461 pub fork_session: bool,
464 pub agents: Option<HashMap<String, AgentDefinition>>,
467}
468
469impl std::fmt::Debug for ClaudeCodeOptions {
470 #[allow(deprecated)]
471 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
472 f.debug_struct("ClaudeCodeOptions")
473 .field("system_prompt", &self.system_prompt)
474 .field("append_system_prompt", &self.append_system_prompt)
475 .field("allowed_tools", &self.allowed_tools)
476 .field("disallowed_tools", &self.disallowed_tools)
477 .field("permission_mode", &self.permission_mode)
478 .field("mcp_servers", &self.mcp_servers)
479 .field("mcp_tools", &self.mcp_tools)
480 .field("max_turns", &self.max_turns)
481 .field("max_thinking_tokens", &self.max_thinking_tokens)
482 .field("max_output_tokens", &self.max_output_tokens)
483 .field("model", &self.model)
484 .field("cwd", &self.cwd)
485 .field("continue_conversation", &self.continue_conversation)
486 .field("resume", &self.resume)
487 .field("permission_prompt_tool_name", &self.permission_prompt_tool_name)
488 .field("settings", &self.settings)
489 .field("add_dirs", &self.add_dirs)
490 .field("extra_args", &self.extra_args)
491 .field("env", &self.env)
492 .field("debug_stderr", &self.debug_stderr.is_some())
493 .field("include_partial_messages", &self.include_partial_messages)
494 .field("can_use_tool", &self.can_use_tool.is_some())
495 .field("hooks", &self.hooks.is_some())
496 .field("control_protocol_format", &self.control_protocol_format)
497 .finish()
498 }
499}
500
501impl ClaudeCodeOptions {
502 pub fn builder() -> ClaudeCodeOptionsBuilder {
504 ClaudeCodeOptionsBuilder::default()
505 }
506}
507
508#[derive(Debug, Default)]
510pub struct ClaudeCodeOptionsBuilder {
511 options: ClaudeCodeOptions,
512}
513
514impl ClaudeCodeOptionsBuilder {
515 #[allow(deprecated)]
517 pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
518 self.options.system_prompt = Some(prompt.into());
519 self
520 }
521
522 #[allow(deprecated)]
524 pub fn append_system_prompt(mut self, prompt: impl Into<String>) -> Self {
525 self.options.append_system_prompt = Some(prompt.into());
526 self
527 }
528
529 pub fn allowed_tools(mut self, tools: Vec<String>) -> Self {
531 self.options.allowed_tools = tools;
532 self
533 }
534
535 pub fn allow_tool(mut self, tool: impl Into<String>) -> Self {
537 self.options.allowed_tools.push(tool.into());
538 self
539 }
540
541 pub fn disallowed_tools(mut self, tools: Vec<String>) -> Self {
543 self.options.disallowed_tools = tools;
544 self
545 }
546
547 pub fn disallow_tool(mut self, tool: impl Into<String>) -> Self {
549 self.options.disallowed_tools.push(tool.into());
550 self
551 }
552
553 pub fn permission_mode(mut self, mode: PermissionMode) -> Self {
555 self.options.permission_mode = mode;
556 self
557 }
558
559 pub fn add_mcp_server(mut self, name: impl Into<String>, config: McpServerConfig) -> Self {
561 self.options.mcp_servers.insert(name.into(), config);
562 self
563 }
564
565 pub fn mcp_servers(mut self, servers: HashMap<String, McpServerConfig>) -> Self {
567 self.options.mcp_servers = servers;
568 self
569 }
570
571 pub fn mcp_tools(mut self, tools: Vec<String>) -> Self {
573 self.options.mcp_tools = tools;
574 self
575 }
576
577 pub fn max_turns(mut self, turns: i32) -> Self {
579 self.options.max_turns = Some(turns);
580 self
581 }
582
583 pub fn max_thinking_tokens(mut self, tokens: i32) -> Self {
585 self.options.max_thinking_tokens = tokens;
586 self
587 }
588
589 pub fn max_output_tokens(mut self, tokens: u32) -> Self {
591 self.options.max_output_tokens = Some(tokens.min(32000).max(1));
592 self
593 }
594
595 pub fn model(mut self, model: impl Into<String>) -> Self {
597 self.options.model = Some(model.into());
598 self
599 }
600
601 pub fn cwd(mut self, path: impl Into<PathBuf>) -> Self {
603 self.options.cwd = Some(path.into());
604 self
605 }
606
607 pub fn continue_conversation(mut self, enable: bool) -> Self {
609 self.options.continue_conversation = enable;
610 self
611 }
612
613 pub fn resume(mut self, id: impl Into<String>) -> Self {
615 self.options.resume = Some(id.into());
616 self
617 }
618
619 pub fn permission_prompt_tool_name(mut self, name: impl Into<String>) -> Self {
621 self.options.permission_prompt_tool_name = Some(name.into());
622 self
623 }
624
625 pub fn settings(mut self, settings: impl Into<String>) -> Self {
627 self.options.settings = Some(settings.into());
628 self
629 }
630
631 pub fn add_dirs(mut self, dirs: Vec<PathBuf>) -> Self {
633 self.options.add_dirs = dirs;
634 self
635 }
636
637 pub fn add_dir(mut self, dir: impl Into<PathBuf>) -> Self {
639 self.options.add_dirs.push(dir.into());
640 self
641 }
642
643 pub fn extra_args(mut self, args: HashMap<String, Option<String>>) -> Self {
645 self.options.extra_args = args;
646 self
647 }
648
649 pub fn add_extra_arg(mut self, key: impl Into<String>, value: Option<String>) -> Self {
651 self.options.extra_args.insert(key.into(), value);
652 self
653 }
654
655 pub fn control_protocol_format(mut self, format: ControlProtocolFormat) -> Self {
657 self.options.control_protocol_format = format;
658 self
659 }
660
661 pub fn include_partial_messages(mut self, include: bool) -> Self {
663 self.options.include_partial_messages = include;
664 self
665 }
666
667 pub fn fork_session(mut self, fork: bool) -> Self {
669 self.options.fork_session = fork;
670 self
671 }
672
673 pub fn setting_sources(mut self, sources: Vec<SettingSource>) -> Self {
675 self.options.setting_sources = Some(sources);
676 self
677 }
678
679 pub fn agents(mut self, agents: HashMap<String, AgentDefinition>) -> Self {
681 self.options.agents = Some(agents);
682 self
683 }
684
685 pub fn build(self) -> ClaudeCodeOptions {
687 self.options
688 }
689}
690
691#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
693#[serde(tag = "type", rename_all = "lowercase")]
694pub enum Message {
695 User {
697 message: UserMessage,
699 },
700 Assistant {
702 message: AssistantMessage,
704 },
705 System {
707 subtype: String,
709 data: serde_json::Value,
711 },
712 Result {
714 subtype: String,
716 duration_ms: i64,
718 duration_api_ms: i64,
720 is_error: bool,
722 num_turns: i32,
724 session_id: String,
726 #[serde(skip_serializing_if = "Option::is_none")]
728 total_cost_usd: Option<f64>,
729 #[serde(skip_serializing_if = "Option::is_none")]
731 usage: Option<serde_json::Value>,
732 #[serde(skip_serializing_if = "Option::is_none")]
734 result: Option<String>,
735 },
736}
737
738#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
740pub struct UserMessage {
741 pub content: String,
743}
744
745#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
747pub struct AssistantMessage {
748 pub content: Vec<ContentBlock>,
750}
751
752pub use Message::Result as ResultMessage;
754pub use Message::System as SystemMessage;
756
757#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
759#[serde(untagged)]
760pub enum ContentBlock {
761 Text(TextContent),
763 Thinking(ThinkingContent),
765 ToolUse(ToolUseContent),
767 ToolResult(ToolResultContent),
769}
770
771#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
773pub struct TextContent {
774 pub text: String,
776}
777
778#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
780pub struct ThinkingContent {
781 pub thinking: String,
783 pub signature: String,
785}
786
787#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
789pub struct ToolUseContent {
790 pub id: String,
792 pub name: String,
794 pub input: serde_json::Value,
796}
797
798#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
800pub struct ToolResultContent {
801 pub tool_use_id: String,
803 #[serde(skip_serializing_if = "Option::is_none")]
805 pub content: Option<ContentValue>,
806 #[serde(skip_serializing_if = "Option::is_none")]
808 pub is_error: Option<bool>,
809}
810
811#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
813#[serde(untagged)]
814pub enum ContentValue {
815 Text(String),
817 Structured(Vec<serde_json::Value>),
819}
820
821#[derive(Debug, Clone, Serialize, Deserialize)]
823pub struct UserContent {
824 pub role: String,
826 pub content: String,
828}
829
830#[derive(Debug, Clone, Serialize, Deserialize)]
832pub struct AssistantContent {
833 pub role: String,
835 pub content: Vec<ContentBlock>,
837}
838
839#[derive(Debug, Clone, Serialize, Deserialize)]
841pub struct SDKControlInterruptRequest {
842 pub subtype: String, }
845
846#[derive(Debug, Clone, Serialize, Deserialize)]
848#[serde(rename_all = "camelCase")]
849pub struct SDKControlPermissionRequest {
850 pub subtype: String, pub tool_name: String,
854 pub input: serde_json::Value,
856 #[serde(skip_serializing_if = "Option::is_none")]
858 pub permission_suggestions: Option<Vec<PermissionUpdate>>,
859 #[serde(skip_serializing_if = "Option::is_none")]
861 pub blocked_path: Option<String>,
862}
863
864#[derive(Debug, Clone, Serialize, Deserialize)]
866pub struct SDKControlInitializeRequest {
867 pub subtype: String, #[serde(skip_serializing_if = "Option::is_none")]
871 pub hooks: Option<HashMap<String, serde_json::Value>>,
872}
873
874#[derive(Debug, Clone, Serialize, Deserialize)]
876#[serde(rename_all = "camelCase")]
877pub struct SDKControlSetPermissionModeRequest {
878 pub subtype: String, pub mode: String,
882}
883
884#[derive(Debug, Clone, Serialize, Deserialize)]
886#[serde(rename_all = "camelCase")]
887pub struct SDKControlSetModelRequest {
888 pub subtype: String, #[serde(skip_serializing_if = "Option::is_none")]
892 pub model: Option<String>,
893}
894
895#[derive(Debug, Clone, Serialize, Deserialize)]
897#[serde(rename_all = "camelCase")]
898pub struct SDKHookCallbackRequest {
899 pub subtype: String, pub callback_id: String,
903 pub input: serde_json::Value,
905 #[serde(skip_serializing_if = "Option::is_none")]
907 pub tool_use_id: Option<String>,
908}
909
910#[derive(Debug, Clone, Serialize, Deserialize)]
912#[serde(rename_all = "camelCase")]
913pub struct SDKControlMcpMessageRequest {
914 pub subtype: String, pub mcp_server_name: String,
918 pub message: serde_json::Value,
920}
921
922#[derive(Debug, Clone, Serialize, Deserialize)]
924#[serde(tag = "type", rename_all = "snake_case")]
925pub enum SDKControlRequest {
926 #[serde(rename = "interrupt")]
928 Interrupt(SDKControlInterruptRequest),
929 #[serde(rename = "can_use_tool")]
931 CanUseTool(SDKControlPermissionRequest),
932 #[serde(rename = "initialize")]
934 Initialize(SDKControlInitializeRequest),
935 #[serde(rename = "set_permission_mode")]
937 SetPermissionMode(SDKControlSetPermissionModeRequest),
938 #[serde(rename = "set_model")]
940 SetModel(SDKControlSetModelRequest),
941 #[serde(rename = "hook_callback")]
943 HookCallback(SDKHookCallbackRequest),
944 #[serde(rename = "mcp_message")]
946 McpMessage(SDKControlMcpMessageRequest),
947}
948
949#[derive(Debug, Clone, Serialize, Deserialize)]
951#[serde(tag = "type", rename_all = "lowercase")]
952pub enum ControlRequest {
953 Interrupt {
955 request_id: String,
957 },
958}
959
960#[derive(Debug, Clone, Serialize, Deserialize)]
962#[serde(tag = "type", rename_all = "lowercase")]
963pub enum ControlResponse {
964 InterruptAck {
966 request_id: String,
968 success: bool,
970 },
971}
972
973#[cfg(test)]
974mod tests {
975 use super::*;
976
977 #[test]
978 fn test_permission_mode_serialization() {
979 let mode = PermissionMode::AcceptEdits;
980 let json = serde_json::to_string(&mode).unwrap();
981 assert_eq!(json, r#""acceptEdits""#);
982
983 let deserialized: PermissionMode = serde_json::from_str(&json).unwrap();
984 assert_eq!(deserialized, mode);
985
986 let plan_mode = PermissionMode::Plan;
988 let plan_json = serde_json::to_string(&plan_mode).unwrap();
989 assert_eq!(plan_json, r#""plan""#);
990
991 let plan_deserialized: PermissionMode = serde_json::from_str(&plan_json).unwrap();
992 assert_eq!(plan_deserialized, plan_mode);
993 }
994
995 #[test]
996 fn test_message_serialization() {
997 let msg = Message::User {
998 message: UserMessage {
999 content: "Hello".to_string(),
1000 },
1001 };
1002
1003 let json = serde_json::to_string(&msg).unwrap();
1004 assert!(json.contains(r#""type":"user""#));
1005 assert!(json.contains(r#""content":"Hello""#));
1006
1007 let deserialized: Message = serde_json::from_str(&json).unwrap();
1008 assert_eq!(deserialized, msg);
1009 }
1010
1011 #[test]
1012 fn test_options_builder() {
1013 let options = ClaudeCodeOptions::builder()
1014 .system_prompt("Test prompt")
1015 .model("claude-3-opus")
1016 .permission_mode(PermissionMode::AcceptEdits)
1017 .allow_tool("read")
1018 .allow_tool("write")
1019 .max_turns(10)
1020 .build();
1021
1022 assert_eq!(options.system_prompt, Some("Test prompt".to_string()));
1023 assert_eq!(options.model, Some("claude-3-opus".to_string()));
1024 assert_eq!(options.permission_mode, PermissionMode::AcceptEdits);
1025 assert_eq!(options.allowed_tools, vec!["read", "write"]);
1026 assert_eq!(options.max_turns, Some(10));
1027 }
1028
1029 #[test]
1030 fn test_extra_args() {
1031 let mut extra_args = HashMap::new();
1032 extra_args.insert("custom-flag".to_string(), Some("value".to_string()));
1033 extra_args.insert("boolean-flag".to_string(), None);
1034
1035 let options = ClaudeCodeOptions::builder()
1036 .extra_args(extra_args.clone())
1037 .add_extra_arg("another-flag", Some("another-value".to_string()))
1038 .build();
1039
1040 assert_eq!(options.extra_args.len(), 3);
1041 assert_eq!(options.extra_args.get("custom-flag"), Some(&Some("value".to_string())));
1042 assert_eq!(options.extra_args.get("boolean-flag"), Some(&None));
1043 assert_eq!(options.extra_args.get("another-flag"), Some(&Some("another-value".to_string())));
1044 }
1045
1046 #[test]
1047 fn test_thinking_content_serialization() {
1048 let thinking = ThinkingContent {
1049 thinking: "Let me think about this...".to_string(),
1050 signature: "sig123".to_string(),
1051 };
1052
1053 let json = serde_json::to_string(&thinking).unwrap();
1054 assert!(json.contains(r#""thinking":"Let me think about this...""#));
1055 assert!(json.contains(r#""signature":"sig123""#));
1056
1057 let deserialized: ThinkingContent = serde_json::from_str(&json).unwrap();
1058 assert_eq!(deserialized.thinking, thinking.thinking);
1059 assert_eq!(deserialized.signature, thinking.signature);
1060 }
1061}