elicitation/primitives/
duration.rs1use crate::{
27 ElicitCommunicator, ElicitError, ElicitErrorKind, ElicitResult, Elicitation, Generator, Prompt,
28 Select, mcp,
29};
30use std::time::Duration;
31
32crate::default_style!(Duration => DurationStyle);
34crate::default_style!(DurationGenerationMode => DurationGenerationModeStyle);
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub enum DurationGenerationMode {
51 Zero,
53 FromSecs(u64),
55 FromMillis(u64),
57 FromMicros(u64),
59 FromNanos(u64),
61}
62
63impl Select for DurationGenerationMode {
64 fn options() -> Vec<Self> {
65 vec![
66 DurationGenerationMode::Zero,
67 DurationGenerationMode::FromSecs(0),
68 DurationGenerationMode::FromMillis(0),
69 DurationGenerationMode::FromMicros(0),
70 DurationGenerationMode::FromNanos(0),
71 ]
72 }
73
74 fn labels() -> Vec<String> {
75 vec![
76 "Zero".to_string(),
77 "From Seconds".to_string(),
78 "From Milliseconds".to_string(),
79 "From Microseconds".to_string(),
80 "From Nanoseconds".to_string(),
81 ]
82 }
83
84 fn from_label(label: &str) -> Option<Self> {
85 match label {
86 "Zero" => Some(DurationGenerationMode::Zero),
87 "From Seconds" => Some(DurationGenerationMode::FromSecs(0)),
88 "From Milliseconds" => Some(DurationGenerationMode::FromMillis(0)),
89 "From Microseconds" => Some(DurationGenerationMode::FromMicros(0)),
90 "From Nanoseconds" => Some(DurationGenerationMode::FromNanos(0)),
91 _ => None,
92 }
93 }
94}
95
96impl Prompt for DurationGenerationMode {
97 fn prompt() -> Option<&'static str> {
98 Some("How should durations be created?")
99 }
100}
101
102impl Elicitation for DurationGenerationMode {
103 type Style = DurationGenerationModeStyle;
104
105 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
106 let params = mcp::select_params(
108 Self::prompt().unwrap_or("Select an option:"),
109 &Self::labels(),
110 );
111
112 let result = communicator
113 .call_tool(
114 rmcp::model::CallToolRequestParams::new(mcp::tool_names::elicit_select())
115 .with_arguments(params),
116 )
117 .await?;
118
119 let value = mcp::extract_value(result)?;
120 let label = mcp::parse_string(value)?;
121
122 let selected = Self::from_label(&label).ok_or_else(|| {
123 ElicitError::new(ElicitErrorKind::ParseError(
124 "Invalid Duration generation mode".to_string(),
125 ))
126 })?;
127
128 match selected {
130 DurationGenerationMode::Zero => Ok(DurationGenerationMode::Zero),
131 DurationGenerationMode::FromSecs(_) => {
132 let secs = u64::elicit(communicator).await?;
133 Ok(DurationGenerationMode::FromSecs(secs))
134 }
135 DurationGenerationMode::FromMillis(_) => {
136 let millis = u64::elicit(communicator).await?;
137 Ok(DurationGenerationMode::FromMillis(millis))
138 }
139 DurationGenerationMode::FromMicros(_) => {
140 let micros = u64::elicit(communicator).await?;
141 Ok(DurationGenerationMode::FromMicros(micros))
142 }
143 DurationGenerationMode::FromNanos(_) => {
144 let nanos = u64::elicit(communicator).await?;
145 Ok(DurationGenerationMode::FromNanos(nanos))
146 }
147 }
148 }
149
150 fn kani_proof() -> proc_macro2::TokenStream {
151 crate::verification::proof_helpers::kani_multi_variant_enum(
152 "DurationGenerationMode",
153 "Zero",
154 )
155 }
156
157 fn verus_proof() -> proc_macro2::TokenStream {
158 crate::verification::proof_helpers::verus_multi_variant_enum("DurationGenerationMode")
159 }
160
161 fn creusot_proof() -> proc_macro2::TokenStream {
162 crate::verification::proof_helpers::creusot_multi_variant_enum("DurationGenerationMode")
163 }
164}
165
166#[derive(Debug, Clone, Copy)]
171#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
172pub struct DurationGenerator {
173 mode: DurationGenerationMode,
174}
175
176impl DurationGenerator {
177 pub fn new(mode: DurationGenerationMode) -> Self {
179 Self { mode }
180 }
181
182 pub fn mode(&self) -> DurationGenerationMode {
184 self.mode
185 }
186}
187
188impl Generator for DurationGenerator {
189 type Target = Duration;
190
191 fn generate(&self) -> Self::Target {
192 match self.mode {
193 DurationGenerationMode::Zero => Duration::ZERO,
194 DurationGenerationMode::FromSecs(secs) => Duration::from_secs(secs),
195 DurationGenerationMode::FromMillis(millis) => Duration::from_millis(millis),
196 DurationGenerationMode::FromMicros(micros) => Duration::from_micros(micros),
197 DurationGenerationMode::FromNanos(nanos) => Duration::from_nanos(nanos),
198 }
199 }
200}
201
202impl Prompt for Duration {
207 fn prompt() -> Option<&'static str> {
208 Some("Choose how to create the duration:")
209 }
210}
211
212impl Elicitation for Duration {
213 type Style = DurationStyle;
214
215 #[tracing::instrument(skip(communicator))]
216 async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
217 tracing::debug!("Eliciting Duration");
218
219 let mode = DurationGenerationMode::elicit(communicator).await?;
221
222 let generator = DurationGenerator::new(mode);
224 let duration = generator.generate();
225
226 tracing::debug!(?duration, mode = ?mode, "Generated Duration");
227 Ok(duration)
228 }
229
230 fn kani_proof() -> proc_macro2::TokenStream {
231 crate::verification::proof_helpers::kani_duration()
232 }
233
234 fn verus_proof() -> proc_macro2::TokenStream {
235 crate::verification::proof_helpers::verus_duration()
236 }
237
238 fn creusot_proof() -> proc_macro2::TokenStream {
239 crate::verification::proof_helpers::creusot_duration()
240 }
241}