Skip to main content

composio_sdk/models/
versioning.rs

1//! Toolkit versioning types and utilities
2//!
3//! This module provides types for managing toolkit versions in the Composio SDK.
4//! It allows you to specify which version of a toolkit to use, either "latest"
5//! or a specific version string like "20250906_01".
6//!
7//! # Examples
8//!
9//! ```rust
10//! use composio_sdk::models::versioning::{ToolkitVersion, ToolkitVersionParam};
11//! use std::collections::HashMap;
12//!
13//! // Use latest version for all toolkits
14//! let config = ToolkitVersionParam::Latest;
15//!
16//! // Use specific versions
17//! let mut versions = HashMap::new();
18//! versions.insert("github".to_string(), ToolkitVersion::Specific("20250906_01".to_string()));
19//! versions.insert("gmail".to_string(), ToolkitVersion::Latest);
20//! let config = ToolkitVersionParam::Versions(versions);
21//!
22//! // Don't specify version (use server default)
23//! let config = ToolkitVersionParam::None;
24//! ```
25
26use serde::{Deserialize, Serialize};
27use std::collections::HashMap;
28
29/// The "latest" version constant
30pub const TOOLKIT_LATEST_VERSION: &str = "latest";
31
32/// Version of a toolkit
33///
34/// A toolkit version can be either "latest" (always use the most recent version)
35/// or a specific version string like "20250906_01".
36///
37/// # Examples
38///
39/// ```rust
40/// use composio_sdk::models::versioning::ToolkitVersion;
41///
42/// // Use latest version
43/// let version = ToolkitVersion::Latest;
44/// assert_eq!(version.as_str(), "latest");
45///
46/// // Use specific version
47/// let version = ToolkitVersion::Specific("20250906_01".to_string());
48/// assert_eq!(version.as_str(), "20250906_01");
49/// ```
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub enum ToolkitVersion {
52    /// Always use the latest version
53    Latest,
54    /// Use a specific version (e.g., "20250906_01")
55    Specific(String),
56}
57
58impl Serialize for ToolkitVersion {
59    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
60    where
61        S: serde::Serializer,
62    {
63        serializer.serialize_str(self.as_str())
64    }
65}
66
67impl<'de> Deserialize<'de> for ToolkitVersion {
68    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
69    where
70        D: serde::Deserializer<'de>,
71    {
72        let s = String::deserialize(deserializer)?;
73        Ok(if s == TOOLKIT_LATEST_VERSION {
74            Self::Latest
75        } else {
76            Self::Specific(s)
77        })
78    }
79}
80
81impl ToolkitVersion {
82    /// Convert the version to a string slice for API calls
83    ///
84    /// # Examples
85    ///
86    /// ```rust
87    /// use composio_sdk::models::versioning::ToolkitVersion;
88    ///
89    /// let latest = ToolkitVersion::Latest;
90    /// assert_eq!(latest.as_str(), "latest");
91    ///
92    /// let specific = ToolkitVersion::Specific("20250906_01".to_string());
93    /// assert_eq!(specific.as_str(), "20250906_01");
94    /// ```
95    pub fn as_str(&self) -> &str {
96        match self {
97            Self::Latest => TOOLKIT_LATEST_VERSION,
98            Self::Specific(version) => version.as_str(),
99        }
100    }
101
102    /// Check if this is the latest version
103    ///
104    /// # Examples
105    ///
106    /// ```rust
107    /// use composio_sdk::models::versioning::ToolkitVersion;
108    ///
109    /// assert!(ToolkitVersion::Latest.is_latest());
110    /// assert!(!ToolkitVersion::Specific("20250906_01".to_string()).is_latest());
111    /// ```
112    pub fn is_latest(&self) -> bool {
113        matches!(self, Self::Latest)
114    }
115
116    /// Check if this is a specific version
117    ///
118    /// # Examples
119    ///
120    /// ```rust
121    /// use composio_sdk::models::versioning::ToolkitVersion;
122    ///
123    /// assert!(!ToolkitVersion::Latest.is_specific());
124    /// assert!(ToolkitVersion::Specific("20250906_01".to_string()).is_specific());
125    /// ```
126    pub fn is_specific(&self) -> bool {
127        matches!(self, Self::Specific(_))
128    }
129}
130
131impl Default for ToolkitVersion {
132    fn default() -> Self {
133        Self::Latest
134    }
135}
136
137impl From<&str> for ToolkitVersion {
138    fn from(s: &str) -> Self {
139        if s == TOOLKIT_LATEST_VERSION {
140            Self::Latest
141        } else {
142            Self::Specific(s.to_string())
143        }
144    }
145}
146
147impl From<String> for ToolkitVersion {
148    fn from(s: String) -> Self {
149        if s == TOOLKIT_LATEST_VERSION {
150            Self::Latest
151        } else {
152            Self::Specific(s)
153        }
154    }
155}
156
157/// Map of toolkit slugs to their versions
158///
159/// This allows you to specify different versions for different toolkits.
160///
161/// # Examples
162///
163/// ```rust
164/// use composio_sdk::models::versioning::{ToolkitVersion, ToolkitVersions};
165/// use std::collections::HashMap;
166///
167/// let mut versions: ToolkitVersions = HashMap::new();
168/// versions.insert("github".to_string(), ToolkitVersion::Specific("20250906_01".to_string()));
169/// versions.insert("gmail".to_string(), ToolkitVersion::Latest);
170/// ```
171pub type ToolkitVersions = HashMap<String, ToolkitVersion>;
172
173/// Parameter for specifying toolkit versions
174///
175/// This enum allows you to specify toolkit versions in three ways:
176/// - `Latest`: Use "latest" for all toolkits
177/// - `Versions`: Specify different versions for different toolkits
178/// - `None`: Don't specify versions (use server default)
179///
180/// # Examples
181///
182/// ```rust
183/// use composio_sdk::models::versioning::{ToolkitVersion, ToolkitVersionParam};
184/// use std::collections::HashMap;
185///
186/// // Use latest for all toolkits
187/// let config = ToolkitVersionParam::Latest;
188///
189/// // Use specific versions
190/// let mut versions = HashMap::new();
191/// versions.insert("github".to_string(), ToolkitVersion::Specific("20250906_01".to_string()));
192/// let config = ToolkitVersionParam::Versions(versions);
193///
194/// // Don't specify (use default)
195/// let config = ToolkitVersionParam::None;
196/// ```
197#[derive(Debug, Clone, PartialEq)]
198pub enum ToolkitVersionParam {
199    /// Use a map of toolkit-specific versions
200    Versions(ToolkitVersions),
201    /// Use "latest" for all toolkits
202    Latest,
203    /// Don't specify versions (use server default)
204    None,
205}
206
207impl Serialize for ToolkitVersionParam {
208    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
209    where
210        S: serde::Serializer,
211    {
212        match self {
213            Self::Versions(map) => map.serialize(serializer),
214            Self::Latest => serializer.serialize_str(TOOLKIT_LATEST_VERSION),
215            Self::None => serializer.serialize_none(),
216        }
217    }
218}
219
220impl<'de> Deserialize<'de> for ToolkitVersionParam {
221    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
222    where
223        D: serde::Deserializer<'de>,
224    {
225        use serde::de::{self, Visitor};
226        use std::fmt;
227
228        struct ToolkitVersionParamVisitor;
229
230        impl<'de> Visitor<'de> for ToolkitVersionParamVisitor {
231            type Value = ToolkitVersionParam;
232
233            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
234                formatter.write_str("a string 'latest', a map of versions, or null")
235            }
236
237            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
238            where
239                E: de::Error,
240            {
241                if value == TOOLKIT_LATEST_VERSION {
242                    Ok(ToolkitVersionParam::Latest)
243                } else {
244                    Err(de::Error::custom(format!(
245                        "expected 'latest' but got '{}'",
246                        value
247                    )))
248                }
249            }
250
251            fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
252            where
253                M: de::MapAccess<'de>,
254            {
255                let versions = ToolkitVersions::deserialize(de::value::MapAccessDeserializer::new(map))?;
256                Ok(ToolkitVersionParam::Versions(versions))
257            }
258
259            fn visit_none<E>(self) -> Result<Self::Value, E>
260            where
261                E: de::Error,
262            {
263                Ok(ToolkitVersionParam::None)
264            }
265
266            fn visit_unit<E>(self) -> Result<Self::Value, E>
267            where
268                E: de::Error,
269            {
270                Ok(ToolkitVersionParam::None)
271            }
272        }
273
274        deserializer.deserialize_any(ToolkitVersionParamVisitor)
275    }
276}
277
278impl Default for ToolkitVersionParam {
279    fn default() -> Self {
280        Self::None
281    }
282}
283
284impl ToolkitVersionParam {
285    /// Check if this is the Latest variant
286    pub fn is_latest(&self) -> bool {
287        matches!(self, Self::Latest)
288    }
289
290    /// Check if this is the None variant
291    pub fn is_none(&self) -> bool {
292        matches!(self, Self::None)
293    }
294
295    /// Check if this is the Versions variant
296    pub fn is_versions(&self) -> bool {
297        matches!(self, Self::Versions(_))
298    }
299
300    /// Get the version for a specific toolkit
301    ///
302    /// # Examples
303    ///
304    /// ```rust
305    /// use composio_sdk::models::versioning::{ToolkitVersion, ToolkitVersionParam};
306    /// use std::collections::HashMap;
307    ///
308    /// let mut versions = HashMap::new();
309    /// versions.insert("github".to_string(), ToolkitVersion::Specific("20250906_01".to_string()));
310    /// let config = ToolkitVersionParam::Versions(versions);
311    ///
312    /// assert_eq!(
313    ///     config.get_version("github").map(|v| v.as_str()),
314    ///     Some("20250906_01")
315    /// );
316    /// assert_eq!(config.get_version("gmail"), None);
317    /// ```
318    pub fn get_version(&self, toolkit_slug: &str) -> Option<&ToolkitVersion> {
319        match self {
320            Self::Versions(map) => map.get(toolkit_slug),
321            Self::Latest | Self::None => None,
322        }
323    }
324}
325
326#[cfg(test)]
327mod tests {
328    use super::*;
329
330    #[test]
331    fn test_toolkit_version_latest() {
332        let version = ToolkitVersion::Latest;
333        assert_eq!(version.as_str(), "latest");
334        assert!(version.is_latest());
335        assert!(!version.is_specific());
336    }
337
338    #[test]
339    fn test_toolkit_version_specific() {
340        let version = ToolkitVersion::Specific("20250906_01".to_string());
341        assert_eq!(version.as_str(), "20250906_01");
342        assert!(!version.is_latest());
343        assert!(version.is_specific());
344    }
345
346    #[test]
347    fn test_toolkit_version_default() {
348        let version = ToolkitVersion::default();
349        assert!(version.is_latest());
350    }
351
352    #[test]
353    fn test_toolkit_version_from_str() {
354        let latest: ToolkitVersion = "latest".into();
355        assert!(latest.is_latest());
356
357        let specific: ToolkitVersion = "20250906_01".into();
358        assert!(specific.is_specific());
359        assert_eq!(specific.as_str(), "20250906_01");
360    }
361
362    #[test]
363    fn test_toolkit_version_from_string() {
364        let latest: ToolkitVersion = "latest".to_string().into();
365        assert!(latest.is_latest());
366
367        let specific: ToolkitVersion = "20250906_01".to_string().into();
368        assert!(specific.is_specific());
369    }
370
371    #[test]
372    fn test_toolkit_version_serialization() {
373        let latest = ToolkitVersion::Latest;
374        let json = serde_json::to_string(&latest).unwrap();
375        // Latest serializes as the string "latest"
376        assert_eq!(json, "\"latest\"");
377
378        let specific = ToolkitVersion::Specific("20250906_01".to_string());
379        let json = serde_json::to_string(&specific).unwrap();
380        assert_eq!(json, "\"20250906_01\"");
381    }
382
383    #[test]
384    fn test_toolkit_version_deserialization() {
385        let json = "\"latest\"";
386        let version: ToolkitVersion = serde_json::from_str(json).unwrap();
387        assert!(version.is_latest());
388
389        let json = "\"20250906_01\"";
390        let version: ToolkitVersion = serde_json::from_str(json).unwrap();
391        assert_eq!(version.as_str(), "20250906_01");
392    }
393
394    #[test]
395    fn test_toolkit_versions_map() {
396        let mut versions: ToolkitVersions = HashMap::new();
397        versions.insert("github".to_string(), ToolkitVersion::Latest);
398        versions.insert(
399            "gmail".to_string(),
400            ToolkitVersion::Specific("20250906_01".to_string()),
401        );
402
403        assert_eq!(versions.len(), 2);
404        assert!(versions.get("github").unwrap().is_latest());
405        assert_eq!(
406            versions.get("gmail").unwrap().as_str(),
407            "20250906_01"
408        );
409    }
410
411    #[test]
412    fn test_toolkit_version_param_latest() {
413        let param = ToolkitVersionParam::Latest;
414        assert!(param.is_latest());
415        assert!(!param.is_none());
416        assert!(!param.is_versions());
417        assert_eq!(param.get_version("github"), None);
418    }
419
420    #[test]
421    fn test_toolkit_version_param_none() {
422        let param = ToolkitVersionParam::None;
423        assert!(!param.is_latest());
424        assert!(param.is_none());
425        assert!(!param.is_versions());
426        assert_eq!(param.get_version("github"), None);
427    }
428
429    #[test]
430    fn test_toolkit_version_param_versions() {
431        let mut versions = HashMap::new();
432        versions.insert("github".to_string(), ToolkitVersion::Latest);
433        versions.insert(
434            "gmail".to_string(),
435            ToolkitVersion::Specific("20250906_01".to_string()),
436        );
437
438        let param = ToolkitVersionParam::Versions(versions);
439        assert!(!param.is_latest());
440        assert!(!param.is_none());
441        assert!(param.is_versions());
442
443        assert!(param.get_version("github").unwrap().is_latest());
444        assert_eq!(
445            param.get_version("gmail").unwrap().as_str(),
446            "20250906_01"
447        );
448        assert_eq!(param.get_version("slack"), None);
449    }
450
451    #[test]
452    fn test_toolkit_version_param_default() {
453        let param = ToolkitVersionParam::default();
454        assert!(param.is_none());
455    }
456
457    #[test]
458    fn test_toolkit_version_param_serialization() {
459        let latest = ToolkitVersionParam::Latest;
460        let json = serde_json::to_string(&latest).unwrap();
461        // Latest serializes as the string "latest"
462        assert_eq!(json, "\"latest\"");
463
464        let none = ToolkitVersionParam::None;
465        let json = serde_json::to_string(&none).unwrap();
466        // None serializes as null
467        assert_eq!(json, "null");
468
469        let mut versions = HashMap::new();
470        versions.insert("github".to_string(), ToolkitVersion::Latest);
471        let param = ToolkitVersionParam::Versions(versions);
472        let json = serde_json::to_string(&param).unwrap();
473        assert!(json.contains("github"));
474        assert!(json.contains("latest"));
475    }
476
477    #[test]
478    fn test_toolkit_version_equality() {
479        let v1 = ToolkitVersion::Latest;
480        let v2 = ToolkitVersion::Latest;
481        assert_eq!(v1, v2);
482
483        let v3 = ToolkitVersion::Specific("20250906_01".to_string());
484        let v4 = ToolkitVersion::Specific("20250906_01".to_string());
485        assert_eq!(v3, v4);
486
487        assert_ne!(v1, v3);
488    }
489
490    #[test]
491    fn test_toolkit_version_clone() {
492        let v1 = ToolkitVersion::Specific("20250906_01".to_string());
493        let v2 = v1.clone();
494        assert_eq!(v1, v2);
495    }
496}