use crate::{
ElicitCommunicator, ElicitError, ElicitErrorKind, ElicitResult, Elicitation, Generator, Prompt,
Select, mcp,
};
use std::time::{Duration, SystemTime};
crate::default_style!(SystemTime => SystemTimeStyle);
crate::default_style!(SystemTimeGenerationMode => SystemTimeGenerationModeStyle);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SystemTimeGenerationMode {
Now,
UnixEpoch,
Offset {
seconds: i64,
nanos: u32,
},
}
impl Select for SystemTimeGenerationMode {
fn options() -> Vec<Self> {
vec![
SystemTimeGenerationMode::Now,
SystemTimeGenerationMode::UnixEpoch,
SystemTimeGenerationMode::Offset {
seconds: 0,
nanos: 0,
},
]
}
fn labels() -> Vec<String> {
vec![
"Now (Current time)".to_string(),
"Unix Epoch (1970-01-01)".to_string(),
"Offset (Custom)".to_string(),
]
}
fn from_label(label: &str) -> Option<Self> {
match label {
"Now (Current time)" => Some(SystemTimeGenerationMode::Now),
"Unix Epoch (1970-01-01)" => Some(SystemTimeGenerationMode::UnixEpoch),
"Offset (Custom)" => Some(SystemTimeGenerationMode::Offset {
seconds: 0,
nanos: 0,
}),
_ => None,
}
}
}
impl Prompt for SystemTimeGenerationMode {
fn prompt() -> Option<&'static str> {
Some("How should system times be generated?")
}
}
impl Elicitation for SystemTimeGenerationMode {
type Style = SystemTimeGenerationModeStyle;
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
let params = mcp::select_params(
Self::prompt().unwrap_or("Select an option:"),
&Self::labels(),
);
let result = communicator
.call_tool(rmcp::model::CallToolRequestParams {
meta: None,
name: mcp::tool_names::elicit_select().into(),
arguments: Some(params),
task: None,
})
.await?;
let value = mcp::extract_value(result)?;
let label = mcp::parse_string(value)?;
let selected = Self::from_label(&label).ok_or_else(|| {
ElicitError::new(ElicitErrorKind::ParseError(
"Invalid SystemTime generation mode".to_string(),
))
})?;
match selected {
SystemTimeGenerationMode::Now => Ok(SystemTimeGenerationMode::Now),
SystemTimeGenerationMode::UnixEpoch => Ok(SystemTimeGenerationMode::UnixEpoch),
SystemTimeGenerationMode::Offset { .. } => {
let seconds = i64::elicit(communicator).await?;
let nanos = u32::elicit(communicator).await?;
Ok(SystemTimeGenerationMode::Offset { seconds, nanos })
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct SystemTimeGenerator {
mode: SystemTimeGenerationMode,
reference: SystemTime,
}
impl SystemTimeGenerator {
pub fn new(mode: SystemTimeGenerationMode) -> Self {
Self {
mode,
reference: SystemTime::now(),
}
}
pub fn with_reference(mode: SystemTimeGenerationMode, reference: SystemTime) -> Self {
Self { mode, reference }
}
pub fn mode(&self) -> SystemTimeGenerationMode {
self.mode
}
pub fn reference(&self) -> SystemTime {
self.reference
}
}
impl Generator for SystemTimeGenerator {
type Target = SystemTime;
fn generate(&self) -> Self::Target {
match self.mode {
SystemTimeGenerationMode::Now => SystemTime::now(),
SystemTimeGenerationMode::UnixEpoch => SystemTime::UNIX_EPOCH,
SystemTimeGenerationMode::Offset { seconds, nanos } => {
let duration = Duration::new(seconds.unsigned_abs(), nanos);
if seconds >= 0 {
self.reference + duration
} else {
self.reference - duration
}
}
}
}
}
impl Prompt for SystemTime {
fn prompt() -> Option<&'static str> {
Some("Choose how to create the system time:")
}
}
impl Elicitation for SystemTime {
type Style = SystemTimeStyle;
#[tracing::instrument(skip(communicator))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting SystemTime");
let mode = SystemTimeGenerationMode::elicit(communicator).await?;
let generator = SystemTimeGenerator::new(mode);
let time = generator.generate();
tracing::debug!(time = ?time, mode = ?mode, "Generated SystemTime");
Ok(time)
}
}