#[macro_export]
macro_rules! select_trenchcoat {
($inner_path:path, as $wrapper_name:ident) => {
$crate::_select_trenchcoat_common!($inner_path, $wrapper_name);
$crate::_select_trenchcoat_manual_serde!($inner_path, $wrapper_name);
};
($inner_path:path, as $wrapper_name:ident, serde) => {
$crate::_select_trenchcoat_common!($inner_path, $wrapper_name);
$crate::_select_trenchcoat_transparent_serde!($inner_path, $wrapper_name);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! _select_trenchcoat_common {
($inner_path:path, $wrapper_name:ident) => {
#[doc = concat!(
"Select-trenchcoat wrapper around [`",
stringify!($inner_path),
"`].\n\n",
"Provides `Serialize`, `Deserialize`, and `JsonSchema` so the\n",
"foreign enum can satisfy `ElicitComplete` and register with MCP tools.\n\n",
"Use `into_inner()` to unwrap back to the original type."
)]
#[derive(Debug, Clone)]
pub struct $wrapper_name($inner_path);
// ── JsonSchema: string enum from Select::labels() ───────────────
impl ::schemars::JsonSchema for $wrapper_name {
fn schema_name() -> ::std::borrow::Cow<'static, str> {
stringify!($wrapper_name).into()
}
fn json_schema(
_gen: &mut ::schemars::SchemaGenerator,
) -> ::schemars::Schema {
let labels = <$inner_path as $crate::Select>::labels();
let enum_values: Vec<::serde_json::Value> = labels
.into_iter()
.map(::serde_json::Value::String)
.collect();
::serde_json::from_value(::serde_json::json!({
"type": "string",
"enum": enum_values,
"description": <$inner_path as $crate::Prompt>::prompt()
.unwrap_or(concat!("Select a ", stringify!($inner_path), " variant"))
}))
.expect("valid JSON Schema")
}
}
impl ::std::convert::From<$inner_path> for $wrapper_name {
fn from(inner: $inner_path) -> Self {
Self(inner)
}
}
impl $wrapper_name {
pub fn into_inner(self) -> $inner_path {
self.0
}
}
impl ::std::ops::Deref for $wrapper_name {
type Target = $inner_path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ::std::convert::AsRef<$inner_path> for $wrapper_name {
fn as_ref(&self) -> &$inner_path {
&self.0
}
}
impl $crate::Prompt for $wrapper_name {
fn prompt() -> ::std::option::Option<&'static str> {
<$inner_path as $crate::Prompt>::prompt()
}
}
impl $crate::Select for $wrapper_name {
fn options() -> ::std::vec::Vec<Self> {
<$inner_path as $crate::Select>::options()
.into_iter()
.map(Self::from)
.collect()
}
fn labels() -> ::std::vec::Vec<::std::string::String> {
<$inner_path as $crate::Select>::labels()
}
fn from_label(label: &str) -> ::std::option::Option<Self> {
<$inner_path as $crate::Select>::from_label(label).map(Self::from)
}
}
impl $crate::Elicitation for $wrapper_name {
type Style = <$inner_path as $crate::Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<C: $crate::ElicitCommunicator>(
communicator: &C,
) -> $crate::ElicitResult<Self> {
<$inner_path as $crate::Elicitation>::elicit(communicator)
.await
.map(Self::from)
}
fn kani_proof() -> proc_macro2::TokenStream {
<$inner_path as $crate::Elicitation>::kani_proof()
}
fn verus_proof() -> proc_macro2::TokenStream {
<$inner_path as $crate::Elicitation>::verus_proof()
}
fn creusot_proof() -> proc_macro2::TokenStream {
<$inner_path as $crate::Elicitation>::creusot_proof()
}
}
impl $crate::ElicitIntrospect for $wrapper_name {
fn pattern() -> $crate::ElicitationPattern {
<$inner_path as $crate::ElicitIntrospect>::pattern()
}
fn metadata() -> $crate::TypeMetadata {
<$inner_path as $crate::ElicitIntrospect>::metadata()
}
}
#[cfg(feature = "prompt-tree")]
impl $crate::ElicitPromptTree for $wrapper_name {
fn prompt_tree() -> $crate::PromptTree {
let labels = <Self as $crate::Select>::labels();
let branch_count = labels.len();
$crate::PromptTree::Select {
prompt: <Self as $crate::Prompt>::prompt()
.unwrap_or(concat!("Select a ", stringify!($wrapper_name), " variant"))
.to_string(),
type_name: stringify!($wrapper_name).to_string(),
options: labels,
branches: vec![None; branch_count],
}
}
}
impl $crate::emit_code::ToCodeLiteral for $wrapper_name {
fn to_code_literal(&self) -> $crate::proc_macro2::TokenStream {
let inner_debug = format!("{:?}", self.0);
let label = <$inner_path as $crate::Select>::labels()
.into_iter()
.zip(<$inner_path as $crate::Select>::options().iter())
.find(|(_, opt)| format!("{:?}", opt) == inner_debug)
.map(|(l, _)| l)
.unwrap_or_else(|| inner_debug.clone());
$crate::quote::quote! {
$wrapper_name::from_label(#label).expect("valid label")
}
}
fn type_tokens() -> $crate::proc_macro2::TokenStream {
$crate::quote::quote! { $wrapper_name }
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! _select_trenchcoat_manual_serde {
($inner_path:path, $wrapper_name:ident) => {
impl ::serde::Serialize for $wrapper_name {
fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
let options = <$inner_path as $crate::Select>::options();
let labels = <$inner_path as $crate::Select>::labels();
let label = options
.iter()
.zip(labels.iter())
.find(|(opt, _)| *opt == &self.0)
.map(|(_, label)| label.as_str())
.unwrap_or("Unknown");
serializer.serialize_str(label)
}
}
impl<'de> ::serde::Deserialize<'de> for $wrapper_name {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
let label =
<::std::string::String as ::serde::Deserialize>::deserialize(deserializer)?;
<$inner_path as $crate::Select>::from_label(&label)
.map(Self::from)
.ok_or_else(|| {
::serde::de::Error::custom(::std::format!(
"invalid {} label: {}",
stringify!($inner_path),
label
))
})
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! _select_trenchcoat_transparent_serde {
($inner_path:path, $wrapper_name:ident) => {
impl ::serde::Serialize for $wrapper_name {
fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
<$inner_path as ::serde::Serialize>::serialize(&self.0, serializer)
}
}
impl<'de> ::serde::Deserialize<'de> for $wrapper_name {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
<$inner_path as ::serde::Deserialize>::deserialize(deserializer).map(Self::from)
}
}
};
}
#[macro_export]
macro_rules! select_trenchcoat_traits {
($name:ident, $inner:path, []) => {};
($name:ident, $inner:path, [$flag:ident $(, $rest:ident)*]) => {
$crate::_select_trenchcoat_trait_flag!($name, $inner, $flag);
$crate::select_trenchcoat_traits!($name, $inner, [$($rest),*]);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! _select_trenchcoat_trait_flag {
($name:ident, $inner:path, copy) => {
impl ::std::marker::Copy for $name {}
};
($name:ident, $inner:path, eq) => {
impl ::std::cmp::PartialEq for $name {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl ::std::cmp::Eq for $name {}
};
($name:ident, $inner:path, hash) => {
impl ::std::hash::Hash for $name {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
};
}