Skip to main content

elicitation/primitives/
unit_structs.rs

1//! Unit struct implementations - the simplest possible elicitation!
2//!
3//! Unit structs have exactly one value, making them trivial to generate.
4//! But they're still useful for type-safe agent outputs, especially when
5//! they have associated methods.
6//!
7//! # Why Elicit Unit Structs?
8//!
9//! Unit structs often carry behavior via associated methods:
10//!
11//! ```rust,ignore
12//! pub struct Validator;
13//!
14//! impl Validator {
15//!     pub fn is_utf8(&self, bytes: &[u8]) -> bool {
16//!         std::str::from_utf8(bytes).is_ok()
17//!     }
18//!     pub fn is_email(&self, s: &str) -> bool {
19//!         s.contains('@')
20//!     }
21//! }
22//! ```
23//!
24//! An agent might need to create a `Validator` instance to use these methods.
25//! Even though there's only one possible value, elicitation provides a
26//! type-safe way for agents to obtain it.
27//!
28//! # Generator Pattern
29//!
30//! ```rust,no_run
31//! use elicitation::{Generator, Elicitation};
32//!
33//! // Unit struct with associated methods
34//! #[derive(Debug, Clone, Copy)]
35//! pub struct Formatter;
36//!
37//! impl Formatter {
38//!     pub fn format_json(&self, s: &str) -> String { s.to_string() }
39//! }
40//!
41//! // Generator is trivial - only one value exists!
42//! // (Would be implemented automatically)
43//! ```
44
45use crate::{ElicitClient, ElicitResult, Elicitation, Generator, Prompt};
46
47// ============================================================================
48// Example Unit Structs
49// ============================================================================
50
51/// Validation helper - unit struct with validation methods.
52///
53/// This demonstrates a unit struct with associated methods. Even though
54/// there's only one value, agents can elicit it to get a type-safe handle.
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub struct Validator;
57
58impl Validator {
59    /// Check if bytes are valid UTF-8.
60    pub fn is_utf8(&self, bytes: &[u8]) -> bool {
61        std::str::from_utf8(bytes).is_ok()
62    }
63
64    /// Check if string is non-empty.
65    pub fn is_non_empty(&self, s: &str) -> bool {
66        !s.is_empty()
67    }
68
69    /// Check if value is in range.
70    pub fn is_in_range<T: PartialOrd>(&self, value: T, min: T, max: T) -> bool {
71        value >= min && value <= max
72    }
73}
74
75/// Formatting helper - unit struct with formatting methods.
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77pub struct Formatter;
78
79impl Formatter {
80    /// Format as uppercase.
81    pub fn uppercase(&self, s: &str) -> String {
82        s.to_uppercase()
83    }
84
85    /// Format as lowercase.
86    pub fn lowercase(&self, s: &str) -> String {
87        s.to_lowercase()
88    }
89
90    /// Trim whitespace.
91    pub fn trim(&self, s: &str) -> String {
92        s.trim().to_string()
93    }
94}
95
96/// Parser helper - unit struct with parsing methods.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
98pub struct Parser;
99
100impl Parser {
101    /// Parse integer.
102    pub fn parse_int(&self, s: &str) -> Result<i64, std::num::ParseIntError> {
103        s.parse()
104    }
105
106    /// Parse float.
107    pub fn parse_float(&self, s: &str) -> Result<f64, std::num::ParseFloatError> {
108        s.parse()
109    }
110
111    /// Parse boolean.
112    pub fn parse_bool(&self, s: &str) -> Result<bool, std::str::ParseBoolError> {
113        s.parse()
114    }
115}
116
117// ============================================================================
118// Trivial Generator Implementation
119// ============================================================================
120//
121// Unit structs have exactly one value - themselves!
122// Generator is trivial: just return Self.
123
124impl Generator for Validator {
125    type Target = Self;
126
127    fn generate(&self) -> Self::Target {
128        Validator
129    }
130}
131
132impl Generator for Formatter {
133    type Target = Self;
134
135    fn generate(&self) -> Self::Target {
136        Formatter
137    }
138}
139
140impl Generator for Parser {
141    type Target = Self;
142
143    fn generate(&self) -> Self::Target {
144        Parser
145    }
146}
147
148// ============================================================================
149// Trivial Elicitation Implementation
150// ============================================================================
151//
152// Since there's only one value, we don't need to ask the agent for input.
153// We can just return the unit struct directly.
154// For consistency, we still show a prompt confirming what's being created.
155
156crate::default_style!(Validator => ValidatorStyle);
157crate::default_style!(Formatter => FormatterStyle);
158crate::default_style!(Parser => ParserStyle);
159
160impl Prompt for Validator {
161    fn prompt() -> Option<&'static str> {
162        Some("Create a Validator instance (unit struct - only one value)")
163    }
164}
165
166impl Elicitation for Validator {
167    type Style = ValidatorStyle;
168
169    async fn elicit(_client: &ElicitClient) -> ElicitResult<Self> {
170        tracing::debug!("Eliciting Validator (unit struct)");
171
172        // Unit struct - only one possible value!
173        // No need to ask the agent for anything.
174        Ok(Validator)
175    }
176}
177
178impl Prompt for Formatter {
179    fn prompt() -> Option<&'static str> {
180        Some("Create a Formatter instance (unit struct - only one value)")
181    }
182}
183
184impl Elicitation for Formatter {
185    type Style = FormatterStyle;
186
187    async fn elicit(_client: &ElicitClient) -> ElicitResult<Self> {
188        tracing::debug!("Eliciting Formatter (unit struct)");
189        Ok(Formatter)
190    }
191}
192
193impl Prompt for Parser {
194    fn prompt() -> Option<&'static str> {
195        Some("Create a Parser instance (unit struct - only one value)")
196    }
197}
198
199impl Elicitation for Parser {
200    type Style = ParserStyle;
201
202    async fn elicit(_client: &ElicitClient) -> ElicitResult<Self> {
203        tracing::debug!("Eliciting Parser (unit struct)");
204        Ok(Parser)
205    }
206}
207
208#[cfg(test)]
209mod tests {
210    use super::*;
211
212    #[test]
213    fn test_validator_methods() {
214        let v = Validator;
215        assert!(v.is_utf8(b"hello"));
216        assert!(!v.is_utf8(b"\xFF\xFE"));
217        assert!(v.is_non_empty("test"));
218        assert!(!v.is_non_empty(""));
219        assert!(v.is_in_range(5, 0, 10));
220        assert!(!v.is_in_range(15, 0, 10));
221    }
222
223    #[test]
224    fn test_formatter_methods() {
225        let f = Formatter;
226        assert_eq!(f.uppercase("hello"), "HELLO");
227        assert_eq!(f.lowercase("WORLD"), "world");
228        assert_eq!(f.trim("  test  "), "test");
229    }
230
231    #[test]
232    fn test_parser_methods() {
233        let p = Parser;
234        assert_eq!(p.parse_int("42").unwrap(), 42);
235        let result = p.parse_float("2.5").unwrap();
236        assert!((result - 2.5_f64).abs() < 0.001);
237        assert!(p.parse_bool("true").unwrap());
238    }
239
240    #[test]
241    fn test_generator_trivial() {
242        let v = Validator;
243        assert_eq!(v.generate(), Validator);
244
245        let f = Formatter;
246        assert_eq!(f.generate(), Formatter);
247
248        let p = Parser;
249        assert_eq!(p.generate(), Parser);
250    }
251}