use crate::{
ElicitCommunicator, ElicitError, ElicitErrorKind, ElicitIntrospect, ElicitResult, Elicitation,
ElicitationPattern, Generator, PatternDetails, Prompt, Select, TypeMetadata, VariantMetadata,
mcp,
};
use uuid::Uuid;
crate::default_style!(Uuid => UuidStyle);
crate::default_style!(UuidGenerationMode => UuidGenerationModeStyle);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UuidGenerationMode {
V4,
Nil,
Max,
}
impl Select for UuidGenerationMode {
fn options() -> Vec<Self> {
vec![
UuidGenerationMode::V4,
UuidGenerationMode::Nil,
UuidGenerationMode::Max,
]
}
fn labels() -> Vec<String> {
vec![
"V4 (Random)".to_string(),
"Nil (All zeros)".to_string(),
"Max (All ones)".to_string(),
]
}
fn from_label(label: &str) -> Option<Self> {
match label {
"V4 (Random)" => Some(UuidGenerationMode::V4),
"Nil (All zeros)" => Some(UuidGenerationMode::Nil),
"Max (All ones)" => Some(UuidGenerationMode::Max),
_ => None,
}
}
}
impl Prompt for UuidGenerationMode {
fn prompt() -> Option<&'static str> {
Some("How should UUIDs be generated?")
}
}
impl Elicitation for UuidGenerationMode {
type Style = UuidGenerationModeStyle;
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::new(mcp::tool_names::elicit_select())
.with_arguments(params),
)
.await?;
let value = mcp::extract_value(result)?;
let label = mcp::parse_string(value)?;
Self::from_label(&label).ok_or_else(|| {
ElicitError::new(ElicitErrorKind::ParseError(
"Invalid UUID generation mode".to_string(),
))
})
}
fn kani_proof() -> proc_macro2::TokenStream {
crate::verification::proof_helpers::kani_select_wrapper("UuidGenerationMode", "V4 (Random)")
}
fn verus_proof() -> proc_macro2::TokenStream {
crate::verification::proof_helpers::verus_select_wrapper(
"UuidGenerationMode",
"V4 (Random)",
)
}
fn creusot_proof() -> proc_macro2::TokenStream {
crate::verification::proof_helpers::creusot_select_wrapper(
"UuidGenerationMode",
"V4 (Random)",
)
}
}
impl ElicitIntrospect for UuidGenerationMode {
fn pattern() -> ElicitationPattern {
ElicitationPattern::Select
}
fn metadata() -> TypeMetadata {
TypeMetadata {
type_name: "UuidGenerationMode",
description: Self::prompt(),
details: PatternDetails::Select {
variants: Self::labels()
.into_iter()
.map(|label| VariantMetadata {
label,
fields: vec![],
})
.collect(),
},
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct UuidGenerator {
mode: UuidGenerationMode,
}
impl UuidGenerator {
pub fn new(mode: UuidGenerationMode) -> Self {
Self { mode }
}
pub fn mode(&self) -> UuidGenerationMode {
self.mode
}
}
impl Generator for UuidGenerator {
type Target = Uuid;
fn generate(&self) -> Self::Target {
match self.mode {
UuidGenerationMode::V4 => Uuid::new_v4(),
UuidGenerationMode::Nil => Uuid::nil(),
UuidGenerationMode::Max => Uuid::max(),
}
}
}
impl Prompt for Uuid {
fn prompt() -> Option<&'static str> {
Some(
"Please provide a UUID (hyphenated format), type 'generate' for a new random UUID, or choose a generation strategy:",
)
}
}
impl Elicitation for Uuid {
type Style = UuidStyle;
#[tracing::instrument(skip(communicator))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting UUID");
let mode = UuidGenerationMode::elicit(communicator).await?;
let generator = UuidGenerator::new(mode);
let uuid = generator.generate();
tracing::debug!(uuid = %uuid, mode = ?mode, "Generated UUID");
Ok(uuid)
}
fn kani_proof() -> proc_macro2::TokenStream {
crate::verification::proof_helpers::kani_trusted_opaque("uuid::Uuid")
}
fn verus_proof() -> proc_macro2::TokenStream {
crate::verification::proof_helpers::verus_type_stub("uuid_Uuid")
}
fn creusot_proof() -> proc_macro2::TokenStream {
crate::verification::proof_helpers::creusot_type_stub("uuid_Uuid")
}
}
impl ElicitIntrospect for Uuid {
fn pattern() -> ElicitationPattern {
ElicitationPattern::Primitive
}
fn metadata() -> TypeMetadata {
TypeMetadata {
type_name: "uuid::Uuid",
description: Self::prompt(),
details: PatternDetails::Primitive,
}
}
}
#[cfg(feature = "prompt-tree")]
impl crate::ElicitPromptTree for Uuid {
fn prompt_tree() -> crate::PromptTree {
crate::PromptTree::Leaf {
prompt: "UUID (e.g. 550e8400-e29b-41d4-a716-446655440000)".to_string(),
type_name: "uuid::Uuid".to_string(),
}
}
}