claude_agent_sdk/types/plugin.rs
1//! Plugin configuration types for Claude Agent SDK
2//!
3//! Plugins allow you to extend Claude Code functionality with custom features,
4//! tools, and integrations. This module provides types for configuring and
5//! loading plugins from local paths.
6
7use serde::{Deserialize, Serialize};
8use std::path::PathBuf;
9
10/// Plugin configuration for extending Claude Code functionality
11///
12/// Plugins can be loaded from local filesystem paths and provide additional
13/// tools, features, or integrations that Claude can use during execution.
14///
15/// # Examples
16///
17/// ```
18/// use claude_agent_sdk::SdkPluginConfig;
19/// use std::path::PathBuf;
20///
21/// // Load a local plugin
22/// let plugin = SdkPluginConfig::Local {
23/// path: PathBuf::from("./my-plugin"),
24/// };
25/// ```
26#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
27#[serde(tag = "type")]
28#[serde(rename_all = "lowercase")]
29pub enum SdkPluginConfig {
30 /// Local filesystem plugin
31 ///
32 /// Loads a plugin from a local directory path. The path should point to
33 /// a valid Claude Code plugin directory structure.
34 ///
35 /// # Example
36 ///
37 /// ```
38 /// use claude_agent_sdk::SdkPluginConfig;
39 /// use std::path::PathBuf;
40 ///
41 /// let plugin = SdkPluginConfig::Local {
42 /// path: PathBuf::from("~/.claude/plugins/my-plugin"),
43 /// };
44 /// ```
45 Local {
46 /// Path to the plugin directory
47 path: PathBuf,
48 },
49}
50
51impl SdkPluginConfig {
52 /// Create a new local plugin configuration
53 ///
54 /// # Examples
55 ///
56 /// ```
57 /// use claude_agent_sdk::SdkPluginConfig;
58 ///
59 /// let plugin = SdkPluginConfig::local("./my-plugin");
60 /// ```
61 pub fn local(path: impl Into<PathBuf>) -> Self {
62 SdkPluginConfig::Local { path: path.into() }
63 }
64
65 /// Get the path for a local plugin
66 ///
67 /// Returns `Some(&PathBuf)` for local plugins, `None` for other types.
68 ///
69 /// # Examples
70 ///
71 /// ```
72 /// use claude_agent_sdk::SdkPluginConfig;
73 ///
74 /// let plugin = SdkPluginConfig::local("./my-plugin");
75 /// assert!(plugin.path().is_some());
76 /// ```
77 pub fn path(&self) -> Option<&PathBuf> {
78 match self {
79 SdkPluginConfig::Local { path } => Some(path),
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_plugin_local_creation() {
90 let plugin = SdkPluginConfig::local("/path/to/plugin");
91 assert!(matches!(plugin, SdkPluginConfig::Local { .. }));
92 }
93
94 #[test]
95 fn test_plugin_path_getter() {
96 let plugin = SdkPluginConfig::local("/path/to/plugin");
97 assert_eq!(plugin.path(), Some(&PathBuf::from("/path/to/plugin")));
98 }
99
100 #[test]
101 fn test_plugin_serialization() {
102 let plugin = SdkPluginConfig::Local {
103 path: PathBuf::from("/test/path"),
104 };
105
106 let json = serde_json::to_value(&plugin).unwrap();
107 assert_eq!(json["type"], "local");
108 assert_eq!(json["path"], "/test/path");
109 }
110
111 #[test]
112 fn test_plugin_deserialization() {
113 let json = serde_json::json!({
114 "type": "local",
115 "path": "/test/path"
116 });
117
118 let plugin: SdkPluginConfig = serde_json::from_value(json).unwrap();
119 assert_eq!(
120 plugin,
121 SdkPluginConfig::Local {
122 path: PathBuf::from("/test/path")
123 }
124 );
125 }
126
127 #[test]
128 fn test_plugin_roundtrip() {
129 let original = SdkPluginConfig::local("~/.claude/plugins/test");
130 let json = serde_json::to_string(&original).unwrap();
131 let deserialized: SdkPluginConfig = serde_json::from_str(&json).unwrap();
132 assert_eq!(original, deserialized);
133 }
134
135 #[test]
136 fn test_plugin_with_relative_path() {
137 let plugin = SdkPluginConfig::local("./plugins/my-plugin");
138 assert_eq!(
139 plugin.path().unwrap().to_str().unwrap(),
140 "./plugins/my-plugin"
141 );
142 }
143
144 #[test]
145 fn test_plugin_with_home_directory() {
146 let plugin = SdkPluginConfig::local("~/my-plugin");
147 assert!(
148 plugin
149 .path()
150 .unwrap()
151 .to_str()
152 .unwrap()
153 .contains("my-plugin")
154 );
155 }
156}