Skip to main content

llm_tool/
types.rs

1//! Core types for `llm-tool`: tool output, errors, context, and definitions.
2//!
3//! Defines the types that tools produce ([`ToolOutput`], [`ToolError`]),
4//! the execution context ([`ToolContext`]) shared across tool calls, and
5//! the [`ToolDefinition`] metadata sent to models.
6
7use alloc::{
8    boxed::Box,
9    format,
10    string::{String, ToString},
11};
12
13/// Re-export the `#[llm_tool]` proc macro for defining tools from plain functions.
14///
15/// # Usage
16///
17/// ```
18/// use llm_tool::{RustTool, ToolContext, ToolRegistry, llm_tool};
19///
20/// /// Adds two numbers together (with a twist).
21/// #[llm_tool]
22/// fn wonky_add(
23///     /// First number.
24///     a: i64,
25///     /// Second number.
26///     b: i64,
27/// ) -> Result<String, String> {
28///     Ok(format!("{}", a + b + 1))
29/// }
30///
31/// let mut registry = ToolRegistry::new();
32/// registry.register(WonkyAdd);
33/// assert_eq!(registry.definitions().len(), 1);
34/// ```
35pub use llm_tool_macros::{llm_prompt, llm_resource, llm_tool};
36// Re-export `JsonSchema` derive so tool authors can write `use llm_tool::JsonSchema;`
37// without adding `schemars` to their own `Cargo.toml`.
38pub use schemars::JsonSchema;
39use serde::{Deserialize, Serialize};
40
41use crate::compat::HashMap;
42
43/// Human-readable JSON type name for error messages.
44const fn other_type_name(value: &serde_json::Value) -> &'static str {
45    match value {
46        serde_json::Value::Null => "null",
47        serde_json::Value::Bool(_) => "bool",
48        serde_json::Value::Number(_) => "number",
49        serde_json::Value::String(_) => "string",
50        serde_json::Value::Array(_) => "array",
51        serde_json::Value::Object(_) => "object",
52    }
53}
54
55/// The return value of a Rust tool execution.
56///
57/// Every tool produces a `ToolOutput` containing:
58/// - **`content`**: the text sent back to the model.
59/// - **`metadata`**: an optional structured key-value map available to hooks,
60///   policies, and logging pipelines — but **never** sent to the model.
61///
62/// # Ergonomics
63///
64/// `ToolOutput` implements `From<String>`, `From<&str>`, and `Display`, so
65/// simple tools can return plain strings without ceremony:
66///
67/// ```rust
68/// use llm_tool::ToolOutput;
69/// use serde::Serialize;
70///
71/// // From a String
72/// let out: ToolOutput = "hello".to_string().into();
73/// assert_eq!(out.content(), "hello");
74/// assert!(out.metadata().is_empty());
75///
76/// // From &str
77/// let out: ToolOutput = "world".into();
78/// assert_eq!(out.content(), "world");
79///
80/// // Structured metadata from a typed struct (preferred)
81/// #[derive(Serialize)]
82/// struct ReadMeta {
83///     bytes_read: usize,
84///     cached: bool,
85/// }
86///
87/// let out = ToolOutput::new("file contents…")
88///     .with_metadata(&ReadMeta {
89///         bytes_read: 1024,
90///         cached: true,
91///     })
92///     .unwrap();
93/// assert_eq!(out.metadata()["bytes_read"], 1024);
94/// assert_eq!(out.metadata()["cached"], true);
95///
96/// // Single ad-hoc entry
97/// let out = ToolOutput::new("done").with_meta("exit_code", serde_json::Value::from(0));
98/// assert_eq!(out.metadata()["exit_code"], 0);
99/// ```
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
101pub struct ToolOutput {
102    /// Text content sent to the LLM.
103    pub(crate) content: String,
104    /// Structured metadata attached to the output.
105    /// NOT sent to the LLM.
106    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
107    pub(crate) metadata: HashMap<String, serde_json::Value>,
108}
109
110impl ToolOutput {
111    /// Create a new `ToolOutput` with the given content and no metadata.
112    pub fn new(content: impl Into<String>) -> Self {
113        Self {
114            content: content.into(),
115            metadata: HashMap::new(),
116        }
117    }
118
119    /// Serialize a value to JSON and wrap it as tool output.
120    ///
121    /// The JSON string becomes the content sent to the model, but no
122    /// metadata is attached. For the zero-redundancy path that populates
123    /// **both** content and metadata from the same struct, use
124    /// [`from_metadata`](Self::from_metadata).
125    ///
126    /// ```rust
127    /// use llm_tool::{ToolError, ToolOutput};
128    /// use serde::Serialize;
129    ///
130    /// #[derive(Serialize)]
131    /// struct Reading {
132    ///     temp: i32,
133    ///     unit: &'static str,
134    /// }
135    ///
136    /// let data = Reading {
137    ///     temp: 72,
138    ///     unit: "F",
139    /// };
140    /// let output = ToolOutput::json(&data).unwrap();
141    /// assert!(output.content().contains("72"));
142    /// assert!(output.metadata().is_empty()); // no metadata attached
143    /// ```
144    ///
145    /// # Errors
146    ///
147    /// Returns `Err(ToolError)` if serialization fails.
148    pub fn json<T: serde::Serialize>(value: &T) -> Result<Self, ToolError> {
149        serde_json::to_string(value)
150            .map(Self::new)
151            .map_err(|e| ToolError::new(format!("serialization failed: {e}")))
152    }
153
154    /// Create a `ToolOutput` where **both** the content and metadata come
155    /// from the same serializable value.
156    ///
157    /// - **Content** (sent to the model): the JSON representation of `value`.
158    /// - **Metadata** (hooks / policies / logging): the flattened object fields.
159    ///
160    /// This is the zero-redundancy path: define one struct, derive
161    /// `Serialize`, and everything is populated automatically.
162    ///
163    /// # Errors
164    ///
165    /// Returns `Err(ToolError)` if `value` doesn't serialize to a JSON object.
166    ///
167    /// # Example
168    ///
169    /// ```rust
170    /// use llm_tool::ToolOutput;
171    /// use serde::Serialize;
172    ///
173    /// #[derive(Serialize)]
174    /// struct Weather {
175    ///     location: String,
176    ///     temp_f: i32,
177    ///     condition: String,
178    /// }
179    ///
180    /// let out = ToolOutput::from_metadata(&Weather {
181    ///     location: "Seattle".into(),
182    ///     temp_f: 72,
183    ///     condition: "Sunny".into(),
184    /// })
185    /// .unwrap();
186    ///
187    /// // Model sees the JSON string
188    /// assert!(out.content().contains("Seattle"));
189    /// assert!(out.content().contains("72"));
190    ///
191    /// // Hooks see typed fields
192    /// assert_eq!(out.metadata()["location"], "Seattle");
193    /// assert_eq!(out.metadata()["temp_f"], 72);
194    /// ```
195    pub fn from_metadata<T: serde::Serialize>(value: &T) -> Result<Self, ToolError> {
196        let json_value = serde_json::to_value(value)
197            .map_err(|e| ToolError::new(format!("metadata serialization failed: {e}")))?;
198        // Serialize to string *before* destructuring so we borrow the Value
199        // instead of cloning the inner Map.
200        let content = json_value.to_string();
201        match json_value {
202            serde_json::Value::Object(map) => Ok(Self {
203                content,
204                metadata: map.into_iter().collect(),
205            }),
206            other => Err(ToolError::new(format!(
207                "metadata must serialize to a JSON object, got {}",
208                other_type_name(&other),
209            ))),
210        }
211    }
212
213    /// Attach a single metadata key-value pair. Chainable.
214    ///
215    /// For attaching multiple fields at once, prefer
216    /// [`with_metadata`](Self::with_metadata) with a typed struct.
217    #[must_use]
218    pub fn with_meta(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
219        self.metadata.insert(key.into(), value);
220        self
221    }
222
223    /// Attach structured metadata from a serializable value.
224    ///
225    /// The value is serialized to a JSON object and its fields are **merged**
226    /// into the metadata map. This is the preferred way to attach metadata
227    /// because it avoids stringly-typed keys and data duplication.
228    ///
229    /// # Errors
230    ///
231    /// Returns `Err(ToolError)` if `value` doesn't serialize to a JSON object
232    /// (e.g. it serializes to a scalar or array).
233    ///
234    /// # Example
235    ///
236    /// ```rust
237    /// use llm_tool::ToolOutput;
238    /// use serde::Serialize;
239    ///
240    /// #[derive(Serialize)]
241    /// struct FileMeta {
242    ///     bytes_read: usize,
243    ///     source: String,
244    /// }
245    ///
246    /// let out = ToolOutput::new("file contents")
247    ///     .with_metadata(&FileMeta {
248    ///         bytes_read: 1024,
249    ///         source: "/etc/hosts".into(),
250    ///     })
251    ///     .unwrap();
252    /// assert_eq!(out.metadata()["bytes_read"], 1024);
253    /// assert_eq!(out.metadata()["source"], "/etc/hosts");
254    /// ```
255    pub fn with_metadata<T: serde::Serialize>(mut self, value: &T) -> Result<Self, ToolError> {
256        let json = serde_json::to_value(value)
257            .map_err(|e| ToolError::new(format!("metadata serialization failed: {e}")))?;
258        match json {
259            serde_json::Value::Object(map) => {
260                self.metadata.extend(map);
261                Ok(self)
262            }
263            other => Err(ToolError::new(format!(
264                "metadata must serialize to a JSON object, got {}",
265                other_type_name(&other),
266            ))),
267        }
268    }
269
270    /// The text content sent back to the model.
271    #[must_use]
272    pub fn content(&self) -> &str {
273        &self.content
274    }
275
276    /// Consume self and return the owned content string.
277    #[must_use]
278    pub fn into_content(self) -> String {
279        self.content
280    }
281
282    /// The structured metadata map.
283    #[must_use]
284    pub const fn metadata(&self) -> &HashMap<String, serde_json::Value> {
285        &self.metadata
286    }
287
288    /// Truncate the output text content according to the given [`TruncationPolicy`].
289    #[must_use]
290    pub fn truncated(mut self, policy: &TruncationPolicy) -> Self {
291        truncation::truncate_output(&mut self, policy);
292        self
293    }
294
295    /// Truncate the output text content according to the given [`TruncationPolicy`],
296    /// returning a detailed [`TruncationOutcome`].
297    ///
298    /// # Errors
299    ///
300    /// Returns [`SpillError`] if an [`OutputSpillSink`] is configured and fails.
301    pub fn truncate_detailed(
302        &mut self,
303        policy: &TruncationPolicy,
304    ) -> Result<TruncationOutcome<'static>, SpillError> {
305        truncation::truncate_output_detailed(self, policy)
306    }
307
308    /// Truncate the output text content with caller context according to [`TruncationPolicy`].
309    ///
310    /// # Errors
311    ///
312    /// Returns [`SpillError`] if an [`OutputSpillSink`] is configured and fails.
313    pub fn truncate_detailed_with_context(
314        &mut self,
315        policy: &TruncationPolicy,
316        ctx: &SpillContext<'_>,
317    ) -> Result<TruncationOutcome<'static>, SpillError> {
318        truncation::truncate_output_detailed_with_context(self, policy, ctx)
319    }
320}
321
322impl core::fmt::Display for ToolOutput {
323    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
324        f.write_str(&self.content)
325    }
326}
327
328impl From<String> for ToolOutput {
329    fn from(content: String) -> Self {
330        Self::new(content)
331    }
332}
333
334impl From<&str> for ToolOutput {
335    fn from(content: &str) -> Self {
336        Self::new(content)
337    }
338}
339
340impl From<i64> for ToolOutput {
341    fn from(value: i64) -> Self {
342        Self::new(value.to_string())
343    }
344}
345
346impl From<f64> for ToolOutput {
347    fn from(value: f64) -> Self {
348        Self::new(value.to_string())
349    }
350}
351
352impl From<bool> for ToolOutput {
353    fn from(value: bool) -> Self {
354        Self::new(value.to_string())
355    }
356}
357
358impl From<serde_json::Value> for ToolOutput {
359    fn from(value: serde_json::Value) -> Self {
360        // serde_json::Value::to_string() never fails.
361        Self::new(value.to_string())
362    }
363}
364
365/// Wrapper for returning serializable values as JSON tool output.
366///
367/// Implements `From<Json<T>> for ToolOutput` so it works with the
368/// `#[llm_tool]` macro's `.into()` conversion — no `Result` wrapper needed
369/// for infallible serialization.
370///
371/// # Panics
372///
373/// The `From` conversion panics if `serde_json::to_string` fails.
374/// This only happens with broken `Serialize` implementations (e.g.,
375/// maps with non-string keys). For explicit error handling, use
376/// [`ToolOutput::json()`] instead.
377///
378/// # Example
379///
380/// ```rust
381/// use llm_tool::{Json, ToolOutput};
382/// use serde::Serialize;
383///
384/// #[derive(Serialize)]
385/// struct Weather {
386///     temp: f64,
387///     city: String,
388/// }
389///
390/// let output: ToolOutput = Json(Weather {
391///     temp: 72.0,
392///     city: "NYC".into(),
393/// })
394/// .into();
395/// assert!(output.content().contains("72"));
396/// ```
397pub struct Json<T>(pub T);
398
399impl<T: serde::Serialize> From<Json<T>> for ToolOutput {
400    fn from(json: Json<T>) -> Self {
401        let json_value = serde_json::to_value(&json.0)
402            .expect("Json<T> serialization failed — this is a bug in the Serialize impl");
403        let content = json_value.to_string();
404        match json_value {
405            serde_json::Value::Object(map) => Self {
406                content,
407                metadata: map.into_iter().collect(),
408            },
409            _ => Self::new(content),
410        }
411    }
412}
413
414/// The category of a registered entity (tool, prompt, or resource).
415///
416/// Used by [`ToolError::not_found`] and the registry dispatchers to
417/// provide clear, type-safe identification of which kind of item was
418/// looked up.
419///
420/// # Example
421///
422/// ```
423/// use llm_tool::RegistryItem;
424///
425/// assert_eq!(RegistryItem::Tool.to_string(), "tool");
426/// assert_eq!(RegistryItem::Prompt.to_string(), "prompt");
427/// assert_eq!(RegistryItem::Resource.to_string(), "resource");
428/// ```
429#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
430pub enum RegistryItem {
431    /// An executable tool.
432    Tool,
433    /// A prompt template.
434    Prompt,
435    /// A readable resource.
436    Resource,
437}
438
439impl core::fmt::Display for RegistryItem {
440    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
441        match self {
442            Self::Tool => write!(f, "tool"),
443            Self::Prompt => write!(f, "prompt"),
444            Self::Resource => write!(f, "resource"),
445        }
446    }
447}
448
449/// An error returned from a tool execution.
450///
451/// The error message is sent back to the model as the tool's error response.
452/// Structured metadata can be attached for hooks and logging — it is **not**
453/// sent to the model.
454///
455/// Implements `From<String>` and `From<&str>` for ergonomic construction.
456///
457/// # Example
458///
459/// ```
460/// use llm_tool::ToolError;
461/// use serde::Serialize;
462///
463/// let err: ToolError = "something went wrong".into();
464/// assert_eq!(err.to_string(), "something went wrong");
465///
466/// let err = ToolError::new(format!("failed to read {}", "file.txt"));
467/// assert!(err.to_string().contains("file.txt"));
468///
469/// // Structured metadata from a typed struct (preferred)
470/// #[derive(Serialize)]
471/// struct HttpErrorMeta {
472///     status_code: u16,
473///     url: String,
474/// }
475///
476/// let err = ToolError::new("HTTP request failed")
477///     .with_metadata(&HttpErrorMeta {
478///         status_code: 503,
479///         url: "https://example.com".into(),
480///     })
481///     .unwrap();
482/// assert_eq!(err.metadata()["status_code"], 503);
483///
484/// // Single ad-hoc entry
485/// let err = ToolError::new("timeout").with_meta("retry_after_secs", serde_json::Value::from(30));
486/// assert_eq!(err.metadata()["retry_after_secs"], 30);
487/// ```
488#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
489pub struct ToolError {
490    /// Human-readable error message sent to the model.
491    pub message: String,
492    /// Structured metadata for hooks / policies / logging.
493    /// NOT sent to the model.
494    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
495    metadata: HashMap<String, serde_json::Value>,
496}
497
498/// Structured category metadata attached to built-in [`ToolError`] constructors.
499#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
500pub struct ErrorKindMetadata {
501    /// Structured error category (`"not_registered"`, `"destructive_in_parallel_batch"`, or `io::ErrorKind`).
502    pub error_kind: String,
503}
504
505/// Structured classification metadata attached when converting [`serde_json::Error`] into [`ToolError`].
506#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
507pub struct JsonErrorCategoryMetadata {
508    /// `serde_json::error::Category` classification (`"Io"`, `"Syntax"`, `"Data"`, `"Eof"`).
509    pub category: String,
510}
511
512impl ToolError {
513    /// Create a new tool error with no metadata.
514    pub fn new(message: impl Into<String>) -> Self {
515        Self {
516            message: message.into(),
517            metadata: HashMap::new(),
518        }
519    }
520
521    /// Metadata key identifying the structured category of an error.
522    pub const ERROR_KIND_KEY: &'static str = "error_kind";
523    /// [`ERROR_KIND_KEY`](Self::ERROR_KIND_KEY) value used by
524    /// [`not_found`](Self::not_found) to mark a registry-lookup miss.
525    pub const KIND_NOT_REGISTERED: &'static str = "not_registered";
526    /// [`ERROR_KIND_KEY`](Self::ERROR_KIND_KEY) value used by
527    /// [`destructive_in_parallel_batch`](Self::destructive_in_parallel_batch) when a
528    /// destructive tool is rejected during parallel batch execution.
529    pub const KIND_DESTRUCTIVE_BATCH: &'static str = "destructive_in_parallel_batch";
530
531    fn with_infallible_metadata<T: serde::Serialize>(self, meta: &T) -> Self {
532        match self.with_metadata(meta) {
533            Ok(err) | Err(err) => err,
534        }
535    }
536
537    /// Construct an error for a registry lookup that found no entry named
538    /// `name` of the given [`RegistryItem`].
539    ///
540    /// The human-readable message is suitable to hand back to the model so it
541    /// can self-correct (e.g. retry with a valid name). An
542    /// [`ERROR_KIND_KEY`](Self::ERROR_KIND_KEY) metadata field is attached —
543    /// never sent to the model — so hosts can still distinguish a routing miss
544    /// from a genuine execution failure for telemetry or policy decisions.
545    /// Prefer [`is_not_found`](Self::is_not_found) over inspecting the metadata
546    /// directly.
547    ///
548    /// # Example
549    ///
550    /// ```
551    /// use llm_tool::{RegistryItem, ToolError};
552    ///
553    /// let err = ToolError::not_found(RegistryItem::Tool, "add_nummbers");
554    /// assert!(err.to_string().contains("add_nummbers"));
555    /// assert!(err.is_not_found());
556    /// ```
557    #[must_use]
558    pub fn not_found(kind: RegistryItem, name: &str) -> Self {
559        Self::new(format!("no {kind} named '{name}' is registered")).with_infallible_metadata(
560            &ErrorKindMetadata {
561                error_kind: Self::KIND_NOT_REGISTERED.to_string(),
562            },
563        )
564    }
565
566    /// Construct a typed error when a tool with [`ToolEffect::Destructive`] is rejected
567    /// in unconfirmed parallel batch dispatch.
568    #[must_use]
569    pub fn destructive_in_parallel_batch(tool_name: &str) -> Self {
570        Self::new(format!(
571            "Tool '{tool_name}' has Destructive effect and cannot be executed in Parallel batch mode without explicit confirmation"
572        ))
573        .with_infallible_metadata(&ErrorKindMetadata {
574            error_kind: Self::KIND_DESTRUCTIVE_BATCH.to_string(),
575        })
576    }
577
578    /// Attach a single metadata key-value pair. Chainable.
579    ///
580    /// For attaching multiple fields at once, prefer
581    /// [`with_metadata`](Self::with_metadata) with a typed struct.
582    #[must_use]
583    pub fn with_meta(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
584        self.metadata.insert(key.into(), value);
585        self
586    }
587
588    /// Attach structured metadata from a serializable value.
589    ///
590    /// The value is serialized to a JSON object and its fields are **merged**
591    /// into the metadata map. See [`ToolOutput::with_metadata`] for details.
592    ///
593    /// # Errors
594    ///
595    /// Returns `Err(self)` if `value` doesn't serialize to a JSON object.
596    pub fn with_metadata<T: serde::Serialize>(mut self, value: &T) -> Result<Self, Self> {
597        let json = match serde_json::to_value(value) {
598            Ok(j) => j,
599            Err(e) => {
600                self.message =
601                    format!("{} (metadata serialization also failed: {e})", self.message);
602                return Err(self);
603            }
604        };
605        match json {
606            serde_json::Value::Object(map) => {
607                self.metadata.extend(map);
608                Ok(self)
609            }
610            other => {
611                self.message = format!(
612                    "{} (metadata must serialize to a JSON object, got {})",
613                    self.message,
614                    other_type_name(&other),
615                );
616                Err(self)
617            }
618        }
619    }
620
621    /// The structured metadata map.
622    #[must_use]
623    pub const fn metadata(&self) -> &HashMap<String, serde_json::Value> {
624        &self.metadata
625    }
626
627    /// Whether this error denotes a registry-lookup miss, i.e. it was produced
628    /// by [`not_found`](Self::not_found).
629    ///
630    /// Lets hosts branch on "the model asked for something that isn't
631    /// registered" versus "a registered handler failed" without matching on
632    /// message strings.
633    ///
634    /// # Example
635    ///
636    /// ```
637    /// use llm_tool::{RegistryItem, ToolError};
638    ///
639    /// assert!(ToolError::not_found(RegistryItem::Prompt, "summarize").is_not_found());
640    /// assert!(!ToolError::new("handler blew up").is_not_found());
641    /// ```
642    #[must_use]
643    pub fn is_not_found(&self) -> bool {
644        matches!(
645            self.metadata.get(Self::ERROR_KIND_KEY),
646            Some(serde_json::Value::String(kind)) if kind == Self::KIND_NOT_REGISTERED
647        )
648    }
649
650    /// Whether this error denotes a [`ToolEffect::Destructive`] tool rejected
651    /// during parallel batch dispatch.
652    #[must_use]
653    pub fn is_destructive_batch_rejected(&self) -> bool {
654        matches!(
655            self.metadata.get(Self::ERROR_KIND_KEY),
656            Some(serde_json::Value::String(kind)) if kind == Self::KIND_DESTRUCTIVE_BATCH
657        )
658    }
659}
660
661impl core::fmt::Display for ToolError {
662    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
663        f.write_str(&self.message)
664    }
665}
666
667impl core::error::Error for ToolError {}
668
669impl From<String> for ToolError {
670    fn from(message: String) -> Self {
671        Self::new(message)
672    }
673}
674
675impl From<&str> for ToolError {
676    fn from(message: &str) -> Self {
677        Self::new(message)
678    }
679}
680
681#[cfg(feature = "std")]
682impl From<std::io::Error> for ToolError {
683    fn from(e: std::io::Error) -> Self {
684        Self::new(e.to_string()).with_infallible_metadata(&ErrorKindMetadata {
685            error_kind: format!("{:?}", e.kind()),
686        })
687    }
688}
689
690impl From<serde_json::Error> for ToolError {
691    fn from(e: serde_json::Error) -> Self {
692        Self::new(e.to_string()).with_infallible_metadata(&JsonErrorCategoryMetadata {
693            category: format!("{:?}", e.classify()),
694        })
695    }
696}
697
698impl From<Box<dyn core::error::Error + Send + Sync>> for ToolError {
699    fn from(e: Box<dyn core::error::Error + Send + Sync>) -> Self {
700        Self::new(e.to_string())
701    }
702}
703
704impl From<core::convert::Infallible> for ToolError {
705    fn from(never: core::convert::Infallible) -> Self {
706        match never {}
707    }
708}
709
710pub mod context;
711pub mod definition;
712pub mod prompt;
713pub mod quarantine;
714pub mod resource;
715pub mod sanitized;
716pub mod surrogate;
717pub mod truncation;
718
719pub use context::*;
720pub use definition::*;
721pub use prompt::*;
722pub use quarantine::*;
723pub use resource::*;
724pub use sanitized::*;
725pub use surrogate::*;
726pub use truncation::*;
727
728#[cfg(all(test, feature = "std"))]
729mod tests;