elicitation 0.10.0

Conversational elicitation of strongly-typed Rust values via MCP
Documentation
//! String type implementation.

use crate::{ElicitCommunicator, ElicitResult, Elicitation, Prompt, Select};

/// Elicitation style variants for String.
///
/// Demonstrates Agent vs Human prompting strategies:
/// - Agent: Terse, assumes technical context
/// - Human: Friendly, more explanatory
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum StringStyle {
    /// Human-friendly prompting (default).
    #[default]
    Human,
    /// Terse agent-oriented prompting.
    Agent,
}

impl Prompt for StringStyle {
    fn prompt() -> Option<&'static str> {
        Some("Select elicitation style:")
    }
}

impl Select for StringStyle {
    fn options() -> Vec<Self> {
        vec![Self::Human, Self::Agent]
    }

    fn labels() -> Vec<String> {
        vec!["human".to_string(), "agent".to_string()]
    }

    fn from_label(label: &str) -> Option<Self> {
        match label {
            "human" => Some(Self::Human),
            "agent" => Some(Self::Agent),
            _ => None,
        }
    }
}

impl Elicitation for StringStyle {
    type Style = ();

    async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
        let prompt = <Self as Prompt>::prompt().unwrap();
        let labels = <Self as Select>::labels();

        // Use Select paradigm with send_prompt (for server-side)
        let formatted_prompt = format!("{}\nOptions: {}", prompt, labels.join(", "));
        let response = communicator.send_prompt(&formatted_prompt).await?;

        <Self as Select>::from_label(response.trim()).ok_or_else(|| {
            crate::ElicitError::from(crate::ElicitErrorKind::ParseError(format!(
                "Invalid style selection: {}",
                response.trim()
            )))
        })
    }

    fn kani_proof() -> proc_macro2::TokenStream {
        crate::verification::proof_helpers::kani_multi_variant_enum("StringStyle", "Human")
    }

    fn verus_proof() -> proc_macro2::TokenStream {
        crate::verification::proof_helpers::verus_multi_variant_enum("StringStyle")
    }

    fn creusot_proof() -> proc_macro2::TokenStream {
        crate::verification::proof_helpers::creusot_multi_variant_enum("StringStyle")
    }
}

impl crate::style::ElicitationStyle for StringStyle {}

impl Prompt for String {
    fn prompt() -> Option<&'static str> {
        Some("Please enter text:")
    }
}

impl Elicitation for String {
    type Style = StringStyle;

    #[tracing::instrument(skip(communicator))]
    async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
        let style = communicator.style_or_elicit::<Self>().await?;

        tracing::debug!(?style, "Eliciting String with style");

        // Style-specific prompting
        let prompt = match style {
            StringStyle::Human => "Please provide a text value:",
            StringStyle::Agent => "Value?",
        };

        // Elicit directly with custom prompt
        communicator.send_prompt(prompt).await
    }

    fn kani_proof() -> proc_macro2::TokenStream {
        crate::verification::proof_helpers::kani_string()
    }

    fn verus_proof() -> proc_macro2::TokenStream {
        crate::verification::proof_helpers::verus_string()
    }

    fn creusot_proof() -> proc_macro2::TokenStream {
        crate::verification::proof_helpers::creusot_string()
    }
}

impl crate::ElicitIntrospect for String {
    fn pattern() -> crate::ElicitationPattern {
        crate::ElicitationPattern::Primitive
    }
    fn metadata() -> crate::TypeMetadata {
        crate::TypeMetadata {
            type_name: "String",
            description: <String as crate::Prompt>::prompt(),
            details: crate::PatternDetails::Primitive,
        }
    }
}