Skip to main content

mcp_compressor_core/compression/
levels.rs

1//! `CompressionLevel` enum — the four verbosity tiers exposed to callers.
2//!
3//! Matches the Python `CompressionLevel` enum in `mcp_compressor/types.py`.
4
5use crate::Error;
6use std::fmt;
7use std::str::FromStr;
8
9/// Verbosity level used when formatting tool listings.
10///
11/// Higher = less token output:
12///
13/// ```text
14/// Low  ← most verbose                    Max ← least verbose
15/// Low  > Medium  > High  > Max
16/// ```
17#[derive(Debug, Clone, PartialEq, Eq, Default)]
18pub enum CompressionLevel {
19    /// Full schema and description (least compressed).
20    Low,
21    /// First sentence of each tool's description only.
22    #[default]
23    Medium,
24    /// Tool name and argument names, no descriptions.
25    High,
26    /// Tool name only; a `list_tools` MCP tool is added to the frontend server.
27    Max,
28}
29
30impl fmt::Display for CompressionLevel {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        let value = match self {
33            Self::Low => "low",
34            Self::Medium => "medium",
35            Self::High => "high",
36            Self::Max => "max",
37        };
38        f.write_str(value)
39    }
40}
41
42impl FromStr for CompressionLevel {
43    type Err = Error;
44
45    fn from_str(s: &str) -> Result<Self, Self::Err> {
46        match s.trim().to_ascii_lowercase().as_str() {
47            "low" => Ok(Self::Low),
48            "medium" => Ok(Self::Medium),
49            "high" => Ok(Self::High),
50            "max" => Ok(Self::Max),
51            _ => Err(Error::UnknownCompressionLevel(s.to_string())),
52        }
53    }
54}
55
56// ---------------------------------------------------------------------------
57// Tests
58// ---------------------------------------------------------------------------
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    // --- FromStr / parsing ---
65
66    /// Parsing "low" (lowercase) produces Low.
67    #[test]
68    fn parse_low() {
69        assert_eq!("low".parse::<CompressionLevel>().unwrap(), CompressionLevel::Low);
70    }
71
72    /// Parsing "medium" produces Medium.
73    #[test]
74    fn parse_medium() {
75        assert_eq!("medium".parse::<CompressionLevel>().unwrap(), CompressionLevel::Medium);
76    }
77
78    /// Parsing "high" produces High.
79    #[test]
80    fn parse_high() {
81        assert_eq!("high".parse::<CompressionLevel>().unwrap(), CompressionLevel::High);
82    }
83
84    /// Parsing "max" produces Max.
85    #[test]
86    fn parse_max() {
87        assert_eq!("max".parse::<CompressionLevel>().unwrap(), CompressionLevel::Max);
88    }
89
90    /// Parsing is case-insensitive: "LOW" and "High" are accepted.
91    #[test]
92    fn parse_case_insensitive() {
93        assert_eq!("LOW".parse::<CompressionLevel>().unwrap(), CompressionLevel::Low);
94        assert_eq!("MEDIUM".parse::<CompressionLevel>().unwrap(), CompressionLevel::Medium);
95        assert_eq!("HIGH".parse::<CompressionLevel>().unwrap(), CompressionLevel::High);
96        assert_eq!("MAX".parse::<CompressionLevel>().unwrap(), CompressionLevel::Max);
97        assert_eq!("High".parse::<CompressionLevel>().unwrap(), CompressionLevel::High);
98    }
99
100    /// An unrecognised string produces an error.
101    #[test]
102    fn parse_invalid() {
103        assert!("invalid".parse::<CompressionLevel>().is_err());
104    }
105
106    /// An empty string produces an error.
107    #[test]
108    fn parse_empty() {
109        assert!("".parse::<CompressionLevel>().is_err());
110    }
111
112    // --- Default ---
113
114    /// The default level is Medium (matches the Python default).
115    #[test]
116    fn default_is_medium() {
117        assert_eq!(CompressionLevel::default(), CompressionLevel::Medium);
118    }
119
120    // --- Display ---
121
122    /// `Display` serialises back to the canonical lowercase form.
123    #[test]
124    fn display_round_trips() {
125        assert_eq!(CompressionLevel::Low.to_string(), "low");
126        assert_eq!(CompressionLevel::Medium.to_string(), "medium");
127        assert_eq!(CompressionLevel::High.to_string(), "high");
128        assert_eq!(CompressionLevel::Max.to_string(), "max");
129    }
130
131    /// A level round-trips through `Display` → `FromStr`.
132    #[test]
133    fn display_then_parse_is_identity() {
134        for level in [
135            CompressionLevel::Low,
136            CompressionLevel::Medium,
137            CompressionLevel::High,
138            CompressionLevel::Max,
139        ] {
140            let serialised = level.to_string();
141            let parsed: CompressionLevel = serialised.parse().unwrap();
142            assert_eq!(parsed, level);
143        }
144    }
145}