Skip to main content

elicitation/
emit_code.rs

1//! Code recovery — emit verified workflows as Rust source.
2//!
3//! [`EmitCode`] is the reverse of the MCP transport layer: where the MCP layer
4//! serializes Rust calls into JSON tool calls for agents, `EmitCode` recovers
5//! the original Rust source from a concrete parameterized invocation.
6//!
7//! # The Core Idea
8//!
9//! Every MCP tool call is a thin wrapper over real Rust. An agent composing
10//! `parse_and_focus → validate_object → pointer_update` has authored a verified
11//! Rust program without knowing it. `EmitCode` materializes that program.
12//!
13//! The emitted source:
14//! - Calls our library's verified APIs directly (not reimplementing logic)
15//! - Preserves the full typestate ceremony: proof tokens, `Established<P>`, etc.
16//! - Compiles against our pinned workspace crates as dependencies
17//! - Inherits formal verification by virtue of calling verified action trait impls
18//!
19//! # Usage
20//!
21//! ```rust,ignore
22//! use elicitation::emit_code::{BinaryScaffold, CrateDep, EmitCode};
23//!
24//! // A workflow params struct implements EmitCode
25//! let step = ParseFocusParams { json: r#"{"name":"Alice"}"#.into(), pointer: "/name".into() };
26//!
27//! let scaffold = BinaryScaffold::new(vec![Box::new(step)], true);
28//! let source = scaffold.to_source(); // formatted Rust source as String
29//! ```
30//!
31//! # Primitive impls
32//!
33//! All numeric primitives, `bool`, `char`, and `String` are covered by an
34//! internal macro that delegates to `quote::ToTokens`. Compound types
35//! (`Vec<T>`, `Option<T>`, `PathBuf`, `Duration`, tuples) have explicit impls.
36
37use proc_macro2::TokenStream;
38
39/// Extensional boundary for `TokenStream` code-literal assembly.
40pub struct CodeLiteralEmitter;
41
42impl CodeLiteralEmitter {
43    /// Parse a concrete type path into tokens.
44    pub fn type_tokens(type_name: &str) -> TokenStream {
45        type_name.parse().expect("valid type tokens")
46    }
47
48    /// Parse a unit enum variant path into tokens.
49    pub fn enum_unit_variant_literal(type_name: &str, variant_name: &str) -> TokenStream {
50        format!("{type_name}::{variant_name}")
51            .parse()
52            .expect("valid unit variant literal")
53    }
54}
55
56// ── Global emit registry ──────────────────────────────────────────────────────
57
58/// An inventory entry connecting a tool name to an [`EmitCode`] constructor.
59///
60/// Register via `inventory::submit!(EmitEntry { ... })` in each crate that
61/// exposes tools with code-recovery support.  The global [`dispatch_emit`]
62/// function collects all registered entries at runtime.
63///
64/// # Example
65///
66/// ```rust,ignore
67/// # use elicitation::emit_code::{EmitEntry, EmitCode};
68/// fn make_my_emit(v: serde_json::Value) -> Result<Box<dyn EmitCode>, String> {
69///     serde_json::from_value::<MyParams>(v)
70///         .map(|p| Box::new(p) as Box<dyn EmitCode>)
71///         .map_err(|e| e.to_string())
72/// }
73///
74/// inventory::submit! {
75///     EmitEntry { tool: "my_tool", constructor: make_my_emit }
76/// }
77/// ```
78pub struct EmitEntry {
79    /// Bare tool name (no namespace prefix), e.g. `"parse_url"`.
80    pub tool: &'static str,
81    /// Crate that registered this entry, e.g. `"elicit_chrono"`.
82    pub crate_name: &'static str,
83    /// Deserialize params from JSON and box as [`EmitCode`].
84    pub constructor: fn(serde_json::Value) -> Result<Box<dyn EmitCode>, String>,
85}
86
87inventory::collect!(EmitEntry);
88
89/// Look up a tool name in the global emit registry and deserialize its params.
90///
91/// Returns a boxed [`EmitCode`] ready to pass to [`BinaryScaffold`], or an
92/// error string if the tool is not registered or params fail to deserialize.
93///
94/// This replaces the per-crate `dispatch_*_emit` chain that was previously
95/// required in [`EmitBinaryPlugin`](crate::plugin::ElicitPlugin).
96pub fn dispatch_emit(tool: &str, params: serde_json::Value) -> Result<Box<dyn EmitCode>, String> {
97    inventory::iter::<EmitEntry>()
98        .find(|e| e.tool == tool)
99        .ok_or_else(|| format!("unknown emit tool: '{tool}'"))
100        .and_then(|e| (e.constructor)(params))
101}
102
103/// Look up a tool registered by a specific crate and deserialize its params.
104///
105/// Use this when multiple crates define a tool with the same name
106/// (e.g. `"assert_future"` in `elicit_chrono`, `elicit_jiff`, `elicit_time`).
107pub fn dispatch_emit_from(
108    tool: &str,
109    crate_name: &str,
110    params: serde_json::Value,
111) -> Result<Box<dyn EmitCode>, String> {
112    inventory::iter::<EmitEntry>()
113        .find(|e| e.tool == tool && e.crate_name == crate_name)
114        .ok_or_else(|| format!("unknown emit tool: '{crate_name}::{tool}'"))
115        .and_then(|e| (e.constructor)(params))
116}
117
118// ── Registration helper macro ─────────────────────────────────────────────────
119
120/// Register a params type with the global emit registry under a tool name.
121///
122/// Generates a named constructor function (to satisfy `inventory`'s requirement
123/// for `'static` function pointers) and submits an [`EmitEntry`].
124///
125/// Only active when the `emit` feature is enabled.
126///
127/// # Example
128///
129/// ```rust,ignore
130/// // In workflow.rs, under #[cfg(feature = "emit")]:
131/// register_emit!("parse_url", ParseUrlParams);
132/// register_emit!("assert_https", AssertHttpsParams);
133/// ```
134#[macro_export]
135macro_rules! register_emit {
136    ($tool:literal, $T:ty) => {
137        const _: () = {
138            fn __emit_constructor(
139                v: elicitation::serde_json::Value,
140            ) -> ::std::result::Result<
141                ::std::boxed::Box<dyn elicitation::emit_code::EmitCode>,
142                ::std::string::String,
143            > {
144                elicitation::serde_json::from_value::<$T>(v)
145                    .map(|p| {
146                        ::std::boxed::Box::new(p)
147                            as ::std::boxed::Box<dyn elicitation::emit_code::EmitCode>
148                    })
149                    .map_err(|e| e.to_string())
150            }
151            elicitation::inventory::submit! {
152                elicitation::emit_code::EmitEntry {
153                    tool: $tool,
154                    crate_name: env!("CARGO_PKG_NAME"),
155                    constructor: __emit_constructor,
156                }
157            }
158        };
159    };
160}
161
162/// Convert a value to a Rust source expression that constructs it.
163///
164/// Unlike [`EmitCode`] which for workflow step types emits a full statement
165/// sequence, `ToCodeLiteral` emits a single *expression* that reproduces this
166/// value. Used by `#[elicit_tool]`-generated `impl EmitCode` blocks to bind
167/// field values.
168pub trait ToCodeLiteral {
169    /// Return a `TokenStream` containing a single Rust expression whose
170    /// evaluation produces a value equal to `self`.
171    fn to_code_literal(&self) -> TokenStream;
172
173    /// Token stream for the concrete type name (used to annotate `None::<T>`).
174    ///
175    /// The default returns `_`, which works when context provides enough
176    /// inference — but for `Option<T>` `None` cases, a concrete type avoids
177    /// "type annotations needed" errors.
178    fn type_tokens() -> TokenStream
179    where
180        Self: Sized,
181    {
182        quote::quote! { _ }
183    }
184}
185
186/// Trait-based escape hatch for handlers the rewriter cannot auto-derive.
187///
188/// Implement this on a zero-sized type and annotate the handler with
189/// `#[elicit_tool(emit = MyType)]`. The macro generates an [`EmitCode`] impl
190/// that delegates `emit_code` to `MyType` and derives `crate_deps` automatically
191/// from the crate's `Cargo.toml`.
192///
193/// # Example
194///
195/// ```rust,ignore
196/// struct FetchJsonEmit;
197/// impl CustomEmit<FetchJsonParams> for FetchJsonEmit {
198///     fn emit_code(params: &FetchJsonParams) -> proc_macro2::TokenStream {
199///         let url = params.url.to_code_literal();
200///         quote::quote! { /* ... */ }
201///     }
202/// }
203/// ```
204pub trait CustomEmit<P> {
205    /// Emit the Rust token stream for this step, given concrete params.
206    fn emit_code(params: &P) -> TokenStream;
207}
208
209/// A type that knows how to recover itself as Rust source code.
210///
211/// Two roles:
212///
213/// - **Value emission** (primitives, std types): emit the literal that produces
214///   this value. `42i32` emits `42i32`. `"hello"` emits `"hello".to_string()`.
215/// - **Step emission** (workflow params): emit the full typestate sequence this
216///   params struct drives. `ParseFocusParams` emits `RawJson::new → .parse() →
217///   .focus() → .extract()`.
218///
219/// Implement this trait for any type whose construction should be recoverable
220/// as part of an emitted binary.
221pub trait EmitCode {
222    /// Emit the Rust token stream for this item.
223    ///
224    /// The emitted code runs in an `async` context with `?` available.
225    /// For value emission, emit a single expression. For step emission,
226    /// emit a statement sequence.
227    fn emit_code(&self) -> TokenStream;
228
229    /// Crate dependencies required by the emitted code.
230    ///
231    /// The default impl returns an empty vec (for primitive/std types that
232    /// need no external deps). Override for types that emit calls into
233    /// workspace crates.
234    fn crate_deps(&self) -> Vec<CrateDep> {
235        vec![]
236    }
237
238    /// Whether this step's emitted code shares the outer function scope with
239    /// adjacent steps.
240    ///
241    /// When `false` (default), `BinaryScaffold` wraps the step in `{ }` so
242    /// local variables and type-inference contexts stay isolated.
243    ///
244    /// When `true`, the step is emitted directly into the function body — its
245    /// bindings (e.g. `let pool = ...`) are visible to subsequent steps.
246    /// Use this for workflow steps that intentionally pass state through
247    /// variable names (e.g. sqlx `connect` → `execute` → `begin` → `commit`).
248    fn shared_scope(&self) -> bool {
249        false
250    }
251}
252
253/// A pre-rendered token stream fragment received across an MCP boundary.
254///
255/// Emit tools return source fragments as plain strings.  When an agent passes
256/// those strings to an [`AssembleParams`](crate::emit_code) step (or nests
257/// one fragment inside another tool's parameters), this wrapper parses the
258/// string back into a live [`TokenStream`] so it can participate in
259/// [`BinaryScaffold`] assembly.
260///
261/// # Example
262///
263/// ```rust
264/// use elicitation::emit_code::{EmitCode, RawFragment};
265///
266/// let fragment = RawFragment("format!(\"x = {}\", value)".into());
267/// let ts = fragment.emit_code();
268/// assert!(!ts.is_empty());
269/// ```
270#[derive(Debug, Clone)]
271pub struct RawFragment(pub String);
272
273impl EmitCode for RawFragment {
274    fn emit_code(&self) -> TokenStream {
275        self.0
276            .parse()
277            .unwrap_or_else(|_| quote::quote!(/* fragment parse error */))
278    }
279
280    fn crate_deps(&self) -> Vec<CrateDep> {
281        vec![]
282    }
283}
284
285/// Parse a Rust expression string into a token stream for literal emission.
286///
287/// This is used by derive-generated `ToCodeLiteral` impls for proxy fields that
288/// store authored Rust expressions as strings but must emit them back as live
289/// syntax rather than string literals.
290pub fn parse_expr_tokens<S>(src: S, context: &str) -> TokenStream
291where
292    S: AsRef<str>,
293{
294    let src = src.as_ref();
295    let expr = syn::parse_str::<syn::Expr>(src)
296        .unwrap_or_else(|error| panic!("invalid {context} expression `{src}`: {error}"));
297    quote::quote! { #expr }
298}
299
300/// A Cargo dependency descriptor with pinned version.
301///
302/// Each `EmitCode` impl that calls into a workspace crate returns `CrateDep`
303/// entries so the scaffold can generate a correct `Cargo.toml`.
304///
305/// Versions are pinned by the impl author (co-located with the `EmitCode`
306/// impl) — we know the correct versions because they are our workspace.
307#[derive(Debug, Clone, PartialEq, Eq, Hash)]
308pub struct CrateDep {
309    /// Crate name as it appears in `Cargo.toml` (e.g. `"elicit_serde_json"`).
310    pub name: &'static str,
311    /// Semver version string (e.g. `"0.8"`).
312    pub version: &'static str,
313    /// Optional feature flags (e.g. `&["full"]`).
314    pub features: &'static [&'static str],
315}
316
317impl CrateDep {
318    /// Construct a dependency with no extra features.
319    pub const fn new(name: &'static str, version: &'static str) -> Self {
320        Self {
321            name,
322            version,
323            features: &[],
324        }
325    }
326
327    /// Construct a dependency with feature flags.
328    pub const fn with_features(
329        name: &'static str,
330        version: &'static str,
331        features: &'static [&'static str],
332    ) -> Self {
333        Self {
334            name,
335            version,
336            features,
337        }
338    }
339
340    /// Render as a TOML dependency line.
341    ///
342    /// ```rust
343    /// use elicitation::emit_code::CrateDep;
344    /// let dep = CrateDep::new("elicit_serde_json", "0.8");
345    /// assert_eq!(dep.to_toml_line(), r#"elicit_serde_json = "0.8""#);
346    /// ```
347    pub fn to_toml_line(&self) -> String {
348        if self.features.is_empty() {
349            format!(r#"{} = "{}""#, self.name, self.version)
350        } else {
351            let feats = self
352                .features
353                .iter()
354                .map(|f| format!(r#""{}""#, f))
355                .collect::<Vec<_>>()
356                .join(", ");
357            format!(
358                r#"{} = {{ version = "{}", features = [{}] }}"#,
359                self.name, self.version, feats
360            )
361        }
362    }
363}
364
365// ── Blanket impl for ToTokens ─────────────────────────────────────────────────
366
367/// Value-emission impl for primitive types via [`quote::ToTokens`].
368///
369/// We enumerate these explicitly (rather than a blanket `impl<T: ToTokens>`)
370/// to avoid conflicts with our specific impls for `Vec`, `Option`, `PathBuf`, etc.
371macro_rules! impl_emit_totokens {
372    ($($T:ty),+ $(,)?) => {
373        $(
374            impl EmitCode for $T {
375                fn emit_code(&self) -> TokenStream {
376                    let mut ts = TokenStream::new();
377                    quote::ToTokens::to_tokens(self, &mut ts);
378                    ts
379                }
380            }
381        )+
382    };
383}
384
385impl_emit_totokens!(
386    bool, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize, f32, f64, char, String,
387);
388
389// ── BinaryScaffold ────────────────────────────────────────────────────────────
390
391/// A sequence of [`EmitCode`] steps wrapped in a `#[tokio::main]` binary scaffold.
392///
393/// Assembles multiple steps (each emitting async statement sequences) into a
394/// complete, compilable Rust program with `main()`, optional tracing init, and
395/// correct `Cargo.toml` dependencies.
396///
397/// # Example
398///
399/// ```rust,ignore
400/// let scaffold = BinaryScaffold::new(vec![Box::new(step1), Box::new(step2)], true);
401/// let source: String = scaffold.to_source(); // pretty-printed Rust
402/// scaffold.emit_to_disk(std::path::Path::new("./output"))?;
403/// ```
404pub struct BinaryScaffold {
405    steps: Vec<Box<dyn EmitCode>>,
406    with_tracing: bool,
407    /// When set, `elicit_*` / `elicitation` deps are emitted as path deps
408    /// instead of crates.io version refs — enabling pre-publish dev/test.
409    ///
410    /// Falls back to the `ELICIT_WORKSPACE_ROOT` environment variable when
411    /// `None`. Integrates cleanly with crates like `config` that manage env.
412    workspace_root: Option<std::path::PathBuf>,
413}
414
415impl BinaryScaffold {
416    /// Create a new scaffold from ordered steps.
417    ///
418    /// - `steps`: Ordered list of [`EmitCode`] items. Each step's emitted code
419    ///   runs sequentially inside `main()`.
420    /// - `with_tracing`: If true, inserts `tracing_subscriber::fmt::init();`
421    ///   at the top of `main()`.
422    pub fn new(steps: Vec<Box<dyn EmitCode>>, with_tracing: bool) -> Self {
423        Self {
424            steps,
425            with_tracing,
426            workspace_root: None,
427        }
428    }
429
430    /// Override elicit workspace crates with local path deps instead of
431    /// crates.io version strings.
432    ///
433    /// Use this during development / pre-publish testing so the emitted
434    /// `Cargo.toml` resolves against your local checkout rather than the
435    /// registry. Falls back to the `ELICIT_WORKSPACE_ROOT` env var when not
436    /// set explicitly.
437    pub fn with_workspace_root(mut self, root: impl Into<std::path::PathBuf>) -> Self {
438        self.workspace_root = Some(root.into());
439        self
440    }
441
442    /// Resolve the effective workspace root: explicit field → env var → None.
443    fn resolved_workspace_root(&self) -> Option<std::path::PathBuf> {
444        self.workspace_root
445            .clone()
446            .or_else(|| std::env::var("ELICIT_WORKSPACE_ROOT").ok().map(Into::into))
447    }
448
449    /// Collect all crate dependencies from all steps, deduplicated by name.
450    ///
451    /// When two steps declare the same crate name, the first declaration wins.
452    /// Always includes `tokio` and `tracing-subscriber` (required by scaffold).
453    pub fn all_deps(&self) -> Vec<CrateDep> {
454        let mut seen = std::collections::HashSet::new();
455        let mut deps = Vec::new();
456
457        // Scaffold always needs these
458        let scaffold_deps = [
459            CrateDep::with_features("tokio", "1", &["full"]),
460            CrateDep::new("tracing-subscriber", "0.3"),
461            CrateDep::new("tracing", "0.1"),
462        ];
463        for dep in scaffold_deps {
464            if seen.insert(dep.name) {
465                deps.push(dep);
466            }
467        }
468
469        for step in &self.steps {
470            for dep in step.crate_deps() {
471                if seen.insert(dep.name) {
472                    deps.push(dep);
473                }
474            }
475        }
476        deps
477    }
478
479    /// Emit the raw token stream for the full `main.rs`.
480    pub fn render(&self) -> TokenStream {
481        let step_tokens: Vec<TokenStream> = self
482            .steps
483            .iter()
484            .map(|s| {
485                let code = s.emit_code();
486                if s.shared_scope() {
487                    // Steps that pass state (like `let pool`) to later steps
488                    // must be emitted directly into the function body. The
489                    // trailing `;` ensures a trailing expression is a statement.
490                    quote::quote! { #code ; }
491                } else {
492                    // Wrap in a block for scope + type-inference isolation.
493                    // This lets steps that end in `Ok(...)` work correctly.
494                    quote::quote! { { #code } }
495                }
496            })
497            .collect();
498
499        let tracing_init = if self.with_tracing {
500            quote::quote! { tracing_subscriber::fmt::init(); }
501        } else {
502            TokenStream::new()
503        };
504
505        // Wildcard imports for workflow crates so their types (e.g. UnvalidatedUrl,
506        // both, Established) are in scope without fully-qualified paths.
507        // `elicitation` uses a sub-module import to avoid shadowing workflow
508        // crate prop structs that share names with elicitation verification types
509        // (e.g. `elicit_reqwest::UrlValid` vs `elicitation::UrlValid`).
510        let mut use_stmts: Vec<TokenStream> = self
511            .all_deps()
512            .into_iter()
513            .filter(|d| d.name.starts_with("elicit"))
514            .map(|d| {
515                let krate: TokenStream = d.name.parse().expect("valid ident");
516                if d.name == "elicitation" {
517                    quote::quote! { use #krate::contracts::*; }
518                } else {
519                    quote::quote! { use #krate::*; }
520                }
521            })
522            .collect();
523
524        // When `reqwest` is a direct dep (e.g. from emit_ctx substitutions that
525        // reference `reqwest::Client::new()`), bring `reqwest::header::HeaderMap`
526        // into scope so handler bodies that call `HeaderMap::new()` compile cleanly
527        // (the `elicit_reqwest::HeaderMap` newtype is a different type).
528        if self.all_deps().iter().any(|d| d.name == "reqwest") {
529            use_stmts.push(quote::quote! { use reqwest::header::HeaderMap; });
530        }
531
532        // `elicit_reqwest` handlers use `HashMap` for headers/query params.
533        if self.all_deps().iter().any(|d| d.name == "elicit_reqwest") {
534            use_stmts.push(quote::quote! { use std::collections::HashMap; });
535        }
536
537        quote::quote! {
538            #( #use_stmts )*
539            #[tokio::main]
540            async fn main() -> Result<(), Box<dyn std::error::Error>> {
541                #tracing_init
542                #( #step_tokens )*
543                Ok(())
544            }
545        }
546    }
547
548    /// Emit formatted Rust source for `main.rs`.
549    ///
550    /// Uses `prettyplease` to format the token stream into readable source.
551    /// Returns an error if the token stream is not valid Rust syntax.
552    pub fn to_source(&self) -> Result<String, syn::Error> {
553        let tokens = self.render();
554        let file: syn::File = syn::parse2(tokens)?;
555        Ok(prettyplease::unparse(&file))
556    }
557
558    /// Emit the `Cargo.toml` content as a string.
559    ///
560    /// When a workspace root is available (via [`Self::with_workspace_root`]
561    /// or the `ELICIT_WORKSPACE_ROOT` env var), any `elicit_*` / `elicitation*`
562    /// dep is emitted as `{ path = "<root>/crates/<name>" }` instead of a
563    /// crates.io version string. All other deps use their declared versions.
564    pub fn to_cargo_toml(&self, package_name: &str) -> String {
565        let ws_root = self.resolved_workspace_root();
566        let deps = self.all_deps();
567        let dep_lines: String = deps
568            .iter()
569            .map(|d| {
570                let line = if let Some(ref root) = ws_root {
571                    if d.name == "elicitation"
572                        || d.name.starts_with("elicit_")
573                        || d.name.starts_with("elicitation_")
574                    {
575                        let path = root.join("crates").join(d.name);
576                        // Use forward slashes — TOML treats `\` as an escape
577                        // character, so Windows paths would be invalid otherwise.
578                        let path_str = path.to_string_lossy().replace('\\', "/");
579                        format!(r#"{} = {{ path = "{}" }}"#, d.name, path_str)
580                    } else {
581                        d.to_toml_line()
582                    }
583                } else {
584                    d.to_toml_line()
585                };
586                format!("{line}\n")
587            })
588            .collect();
589
590        format!(
591            r#"[package]
592name = "{}"
593version = "0.1.0"
594edition = "2021"
595
596# Prevent cargo from treating this as a member of any parent workspace.
597[workspace]
598
599[dependencies]
600{}
601"#,
602            package_name, dep_lines
603        )
604    }
605
606    /// Write `src/main.rs` and `Cargo.toml` to `output_dir`.
607    ///
608    /// Creates the directory structure if it does not exist.
609    /// Returns the path to `src/main.rs` on success.
610    pub fn emit_to_disk(
611        &self,
612        output_dir: &std::path::Path,
613        package_name: &str,
614    ) -> Result<std::path::PathBuf, EmitError> {
615        let src_dir = output_dir.join("src");
616        std::fs::create_dir_all(&src_dir)?;
617
618        let source = self.to_source().map_err(EmitError::Syntax)?;
619        let main_rs = src_dir.join("main.rs");
620        std::fs::write(&main_rs, &source)?;
621
622        let cargo_toml = output_dir.join("Cargo.toml");
623        std::fs::write(&cargo_toml, self.to_cargo_toml(package_name))?;
624
625        Ok(main_rs)
626    }
627}
628
629// ── Artifact compilation ──────────────────────────────────────────────────────
630
631/// Compile the generated project with `cargo build --release`.
632///
633/// Returns the path to the compiled binary on success, or a [`CompileError`]
634/// containing stderr output on failure.
635pub fn compile(project_dir: &std::path::Path) -> Result<std::path::PathBuf, CompileError> {
636    let output = std::process::Command::new("cargo")
637        .args(["build", "--release"])
638        .current_dir(project_dir)
639        .output()
640        .map_err(|e| CompileError::Io(e.to_string()))?;
641
642    if output.status.success() {
643        // Conventional release binary location
644        let binary = project_dir.join("target/release").join(
645            project_dir
646                .file_name()
647                .unwrap_or(std::ffi::OsStr::new("generated_workflow")),
648        );
649        Ok(binary)
650    } else {
651        Err(CompileError::CargoFailed(
652            String::from_utf8_lossy(&output.stderr).into_owned(),
653        ))
654    }
655}
656
657// ── Error types ───────────────────────────────────────────────────────────────
658
659/// Error emitting source to disk.
660#[derive(Debug, derive_more::Display, derive_more::Error)]
661pub enum EmitError {
662    /// The emitted token stream was not valid Rust syntax.
663    #[display("Syntax error in emitted code: {}", _0)]
664    Syntax(#[error(not(source))] syn::Error),
665    /// File system error writing source or Cargo.toml.
666    #[display("IO error: {}", _0)]
667    Io(#[error(not(source))] std::io::Error),
668}
669
670impl From<std::io::Error> for EmitError {
671    fn from(e: std::io::Error) -> Self {
672        EmitError::Io(e)
673    }
674}
675
676/// Error compiling the generated project.
677#[derive(Debug, derive_more::Display, derive_more::Error)]
678pub enum CompileError {
679    /// `cargo build` exited with non-zero status. Contains stderr.
680    #[display("Compilation failed:\n{}", _0)]
681    CargoFailed(#[error(not(source))] String),
682    /// Could not spawn the `cargo` process.
683    #[display("Could not launch cargo: {}", _0)]
684    Io(#[error(not(source))] String),
685}
686
687// ── Specific impls for std types not covered by blanket ───────────────────────
688
689/// `Vec<T>` emits `vec![elem0, elem1, ...]`
690impl<T: EmitCode> EmitCode for Vec<T> {
691    fn emit_code(&self) -> TokenStream {
692        let elems: Vec<TokenStream> = self.iter().map(|e| e.emit_code()).collect();
693        quote::quote! { vec![ #( #elems ),* ] }
694    }
695}
696
697/// `Option<T>` emits `Some(inner)` or `None`
698impl<T: EmitCode> EmitCode for Option<T> {
699    fn emit_code(&self) -> TokenStream {
700        match self {
701            Some(inner) => {
702                let inner_ts = inner.emit_code();
703                quote::quote! { Some(#inner_ts) }
704            }
705            None => quote::quote! { None },
706        }
707    }
708}
709
710/// `PathBuf` emits `std::path::PathBuf::from("...")`
711impl EmitCode for std::path::PathBuf {
712    fn emit_code(&self) -> TokenStream {
713        let s = self.to_string_lossy();
714        let s = s.as_ref();
715        quote::quote! { std::path::PathBuf::from(#s) }
716    }
717}
718
719/// `std::time::Duration` emits `std::time::Duration::from_nanos(n)`
720impl EmitCode for std::time::Duration {
721    fn emit_code(&self) -> TokenStream {
722        let nanos = self.as_nanos() as u64;
723        quote::quote! { std::time::Duration::from_nanos(#nanos) }
724    }
725}
726
727// Tuples — macro to stamp out (A, B), (A, B, C), (A, B, C, D)
728macro_rules! impl_emit_tuple {
729    ( $( $T:ident ),+ ; $( $idx:tt ),+ ) => {
730        impl< $( $T: EmitCode ),+ > EmitCode for ( $( $T, )+ ) {
731            fn emit_code(&self) -> TokenStream {
732                paste::paste! {
733                    $( let [<$T:lower _val>] = self.$idx.emit_code(); )+
734                    quote::quote! { ( $( #[<$T:lower _val>] ),+ ) }
735                }
736            }
737        }
738    };
739}
740
741impl_emit_tuple!(A, B; 0, 1);
742impl_emit_tuple!(A, B, C; 0, 1, 2);
743impl_emit_tuple!(A, B, C, D; 0, 1, 2, 3);
744
745// ── Feature-gated impls ───────────────────────────────────────────────────────
746
747/// `serde_json::Value` emits `serde_json::json!(...)` via the literal repr.
748#[cfg(feature = "serde_json")]
749impl EmitCode for serde_json::Value {
750    fn emit_code(&self) -> TokenStream {
751        let s = self.to_string();
752        quote::quote! {
753            serde_json::from_str(#s).expect("valid json literal")
754        }
755    }
756}
757
758/// `url::Url` emits `url::Url::parse("...").unwrap()`
759#[cfg(feature = "url")]
760impl EmitCode for url::Url {
761    fn emit_code(&self) -> TokenStream {
762        let s = self.as_str();
763        quote::quote! { url::Url::parse(#s).expect("valid URL") }
764    }
765}
766
767/// `uuid::Uuid` emits `uuid::Uuid::parse_str("...").unwrap()`
768#[cfg(feature = "uuid")]
769impl EmitCode for uuid::Uuid {
770    fn emit_code(&self) -> TokenStream {
771        let s = self.to_string();
772        quote::quote! { uuid::Uuid::parse_str(#s).expect("valid UUID") }
773    }
774}
775
776/// `std::net::IpAddr` emits `"...".parse::<std::net::IpAddr>().unwrap()`
777impl EmitCode for std::net::IpAddr {
778    fn emit_code(&self) -> TokenStream {
779        let s = self.to_string();
780        quote::quote! { #s.parse::<std::net::IpAddr>().expect("valid IP") }
781    }
782}
783
784/// `std::net::Ipv4Addr`
785impl EmitCode for std::net::Ipv4Addr {
786    fn emit_code(&self) -> TokenStream {
787        let s = self.to_string();
788        quote::quote! { #s.parse::<std::net::Ipv4Addr>().expect("valid IPv4") }
789    }
790}
791
792/// `std::net::Ipv6Addr`
793impl EmitCode for std::net::Ipv6Addr {
794    fn emit_code(&self) -> TokenStream {
795        let s = self.to_string();
796        quote::quote! { #s.parse::<std::net::Ipv6Addr>().expect("valid IPv6") }
797    }
798}
799
800/// `chrono::DateTime<Utc>` emits RFC 3339 parse
801#[cfg(feature = "chrono")]
802impl EmitCode for chrono::DateTime<chrono::Utc> {
803    fn emit_code(&self) -> TokenStream {
804        let s = self.to_rfc3339();
805        quote::quote! {
806            chrono::DateTime::parse_from_rfc3339(#s)
807                .expect("valid RFC3339 datetime")
808                .with_timezone(&chrono::Utc)
809        }
810    }
811}
812
813/// `chrono::NaiveDateTime`
814#[cfg(feature = "chrono")]
815impl EmitCode for chrono::NaiveDateTime {
816    fn emit_code(&self) -> TokenStream {
817        let s = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string();
818        quote::quote! {
819            chrono::NaiveDateTime::parse_from_str(#s, "%Y-%m-%dT%H:%M:%S%.f")
820                .expect("valid NaiveDateTime")
821        }
822    }
823}
824
825/// `time::OffsetDateTime`
826#[cfg(feature = "time")]
827impl EmitCode for time::OffsetDateTime {
828    fn emit_code(&self) -> TokenStream {
829        let s = self
830            .format(&time::format_description::well_known::Rfc3339)
831            .unwrap_or_default();
832        quote::quote! {
833            time::OffsetDateTime::parse(#s, &time::format_description::well_known::Rfc3339)
834                .expect("valid OffsetDateTime")
835        }
836    }
837}
838
839/// `time::PrimitiveDateTime`
840#[cfg(feature = "time")]
841impl EmitCode for time::PrimitiveDateTime {
842    fn emit_code(&self) -> TokenStream {
843        // Iso8601::DEFAULT uses FormattedComponents::DateTimeOffset, which PrimitiveDateTime
844        // cannot provide. Use a DateTime-only config instead.
845        const PRIM_FMT: time::format_description::well_known::Iso8601<
846            {
847                time::format_description::well_known::iso8601::Config::DEFAULT
848                .set_formatted_components(
849                    time::format_description::well_known::iso8601::FormattedComponents::DateTime,
850                )
851                .encode()
852            },
853        > = time::format_description::well_known::Iso8601;
854        let s = self.format(&PRIM_FMT).unwrap_or_default();
855        quote::quote! {
856            {
857                const PRIM_FMT: time::format_description::well_known::Iso8601<{
858                    time::format_description::well_known::iso8601::Config::DEFAULT
859                        .set_formatted_components(
860                            time::format_description::well_known::iso8601::FormattedComponents::DateTime,
861                        )
862                        .encode()
863                }> = time::format_description::well_known::Iso8601;
864                time::PrimitiveDateTime::parse(#s, &PRIM_FMT).expect("valid PrimitiveDateTime")
865            }
866        }
867    }
868}
869
870/// `time::Time`
871#[cfg(feature = "time")]
872impl EmitCode for time::Time {
873    fn emit_code(&self) -> TokenStream {
874        let h = self.hour();
875        let m = self.minute();
876        let s = self.second();
877        let ns = self.nanosecond();
878        quote::quote! {
879            time::Time::from_hms_nano(#h, #m, #s, #ns).expect("valid Time")
880        }
881    }
882}
883
884/// `jiff::Timestamp`
885#[cfg(feature = "jiff")]
886impl EmitCode for jiff::Timestamp {
887    fn emit_code(&self) -> TokenStream {
888        let s = self.to_string();
889        quote::quote! {
890            #s.parse::<jiff::Timestamp>().expect("valid Timestamp")
891        }
892    }
893}
894
895/// `jiff::Zoned` — emits parse from its Display representation.
896#[cfg(feature = "jiff")]
897impl EmitCode for jiff::Zoned {
898    fn emit_code(&self) -> TokenStream {
899        let s = self.to_string();
900        quote::quote! {
901            #s.parse::<jiff::Zoned>().expect("valid Zoned")
902        }
903    }
904}
905
906/// `jiff::civil::DateTime`
907#[cfg(feature = "jiff")]
908impl EmitCode for jiff::civil::DateTime {
909    fn emit_code(&self) -> TokenStream {
910        let s = self.to_string();
911        quote::quote! {
912            #s.parse::<jiff::civil::DateTime>().expect("valid civil DateTime")
913        }
914    }
915}
916
917/// `reqwest::StatusCode`
918#[cfg(feature = "reqwest")]
919impl EmitCode for reqwest::StatusCode {
920    fn emit_code(&self) -> TokenStream {
921        let n = self.as_u16();
922        quote::quote! {
923            reqwest::StatusCode::from_u16(#n).expect("valid status code")
924        }
925    }
926}
927
928// ── ToCodeLiteral impls ───────────────────────────────────────────────────────
929
930macro_rules! impl_to_code_literal_totokens {
931    ($($T:ty),+ $(,)?) => {
932        $(
933            impl ToCodeLiteral for $T {
934                fn to_code_literal(&self) -> TokenStream {
935                    let mut ts = TokenStream::new();
936                    quote::ToTokens::to_tokens(self, &mut ts);
937                    ts
938                }
939                fn type_tokens() -> TokenStream {
940                    quote::quote! { $T }
941                }
942            }
943        )+
944    };
945}
946
947impl_to_code_literal_totokens!(
948    bool, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize, f32, f64, char,
949);
950
951impl ToCodeLiteral for String {
952    fn to_code_literal(&self) -> TokenStream {
953        let s = self.as_str();
954        quote::quote! { #s.to_string() }
955    }
956    fn type_tokens() -> TokenStream {
957        quote::quote! { String }
958    }
959}
960
961impl<T: ToCodeLiteral> ToCodeLiteral for Option<T> {
962    fn to_code_literal(&self) -> TokenStream {
963        match self {
964            Some(v) => {
965                let inner = v.to_code_literal();
966                quote::quote! { ::std::option::Option::Some(#inner) }
967            }
968            None => {
969                let t = <T as ToCodeLiteral>::type_tokens();
970                quote::quote! { None::<#t> }
971            }
972        }
973    }
974}
975
976impl<T: ToCodeLiteral> ToCodeLiteral for Vec<T> {
977    fn type_tokens() -> TokenStream {
978        let t = <T as ToCodeLiteral>::type_tokens();
979        quote::quote! { ::std::vec::Vec<#t> }
980    }
981
982    fn to_code_literal(&self) -> TokenStream {
983        let elems: Vec<_> = self.iter().map(|v| v.to_code_literal()).collect();
984        quote::quote! { ::std::vec![#(#elems),*] }
985    }
986}
987
988impl<T: ToCodeLiteral, const N: usize> ToCodeLiteral for [T; N] {
989    fn type_tokens() -> TokenStream {
990        let t = <T as ToCodeLiteral>::type_tokens();
991        let n = proc_macro2::Literal::usize_suffixed(N);
992        quote::quote! { [#t; #n] }
993    }
994
995    fn to_code_literal(&self) -> TokenStream {
996        let elements: Vec<_> = self.iter().map(|e| e.to_code_literal()).collect();
997        quote::quote! { [#(#elements),*] }
998    }
999}
1000
1001impl<V: ToCodeLiteral> ToCodeLiteral for std::collections::HashMap<String, V> {
1002    fn type_tokens() -> TokenStream {
1003        let v = <V as ToCodeLiteral>::type_tokens();
1004        quote::quote! { ::std::collections::HashMap<::std::string::String, #v> }
1005    }
1006
1007    fn to_code_literal(&self) -> TokenStream {
1008        let entries: Vec<_> = self
1009            .iter()
1010            .map(|(k, v)| {
1011                let v_ts = v.to_code_literal();
1012                quote::quote! { (#k.to_string(), #v_ts) }
1013            })
1014            .collect();
1015        quote::quote! {
1016            [#(#entries),*].into_iter().collect::<::std::collections::HashMap<_, _>>()
1017        }
1018    }
1019}
1020
1021impl<V: ToCodeLiteral> ToCodeLiteral for std::collections::BTreeMap<String, V> {
1022    fn type_tokens() -> TokenStream {
1023        let v = <V as ToCodeLiteral>::type_tokens();
1024        quote::quote! { ::std::collections::BTreeMap<::std::string::String, #v> }
1025    }
1026
1027    fn to_code_literal(&self) -> TokenStream {
1028        let entries: Vec<_> = self
1029            .iter()
1030            .map(|(k, v)| {
1031                let v_ts = v.to_code_literal();
1032                quote::quote! { (#k.to_string(), #v_ts) }
1033            })
1034            .collect();
1035        quote::quote! {
1036            [#(#entries),*].into_iter().collect::<::std::collections::BTreeMap<_, _>>()
1037        }
1038    }
1039}
1040
1041impl<T: ToCodeLiteral> ToCodeLiteral for Box<T> {
1042    fn type_tokens() -> TokenStream {
1043        let inner = <T as ToCodeLiteral>::type_tokens();
1044        quote::quote! { ::std::boxed::Box<#inner> }
1045    }
1046
1047    fn to_code_literal(&self) -> TokenStream {
1048        let inner = (**self).to_code_literal();
1049        quote::quote! { ::std::boxed::Box::new(#inner) }
1050    }
1051}
1052
1053impl<A: ToCodeLiteral, B: ToCodeLiteral> ToCodeLiteral for (A, B) {
1054    fn type_tokens() -> TokenStream {
1055        let a = <A as ToCodeLiteral>::type_tokens();
1056        let b = <B as ToCodeLiteral>::type_tokens();
1057        quote::quote! { (#a, #b) }
1058    }
1059
1060    fn to_code_literal(&self) -> TokenStream {
1061        let a = self.0.to_code_literal();
1062        let b = self.1.to_code_literal();
1063        quote::quote! { (#a, #b) }
1064    }
1065}
1066
1067impl<A: ToCodeLiteral, B: ToCodeLiteral, C: ToCodeLiteral> ToCodeLiteral for (A, B, C) {
1068    fn type_tokens() -> TokenStream {
1069        let a = <A as ToCodeLiteral>::type_tokens();
1070        let b = <B as ToCodeLiteral>::type_tokens();
1071        let c = <C as ToCodeLiteral>::type_tokens();
1072        quote::quote! { (#a, #b, #c) }
1073    }
1074
1075    fn to_code_literal(&self) -> TokenStream {
1076        let a = self.0.to_code_literal();
1077        let b = self.1.to_code_literal();
1078        let c = self.2.to_code_literal();
1079        quote::quote! { (#a, #b, #c) }
1080    }
1081}
1082
1083impl<A: ToCodeLiteral, B: ToCodeLiteral, C: ToCodeLiteral, D: ToCodeLiteral> ToCodeLiteral
1084    for (A, B, C, D)
1085{
1086    fn type_tokens() -> TokenStream {
1087        let a = <A as ToCodeLiteral>::type_tokens();
1088        let b = <B as ToCodeLiteral>::type_tokens();
1089        let c = <C as ToCodeLiteral>::type_tokens();
1090        let d = <D as ToCodeLiteral>::type_tokens();
1091        quote::quote! { (#a, #b, #c, #d) }
1092    }
1093
1094    fn to_code_literal(&self) -> TokenStream {
1095        let a = self.0.to_code_literal();
1096        let b = self.1.to_code_literal();
1097        let c = self.2.to_code_literal();
1098        let d = self.3.to_code_literal();
1099        quote::quote! { (#a, #b, #c, #d) }
1100    }
1101}
1102
1103// ToCodeLiteral for std types that already have EmitCode: delegate directly.
1104
1105impl ToCodeLiteral for std::net::IpAddr {
1106    fn to_code_literal(&self) -> TokenStream {
1107        EmitCode::emit_code(self)
1108    }
1109}
1110
1111impl ToCodeLiteral for std::net::Ipv4Addr {
1112    fn to_code_literal(&self) -> TokenStream {
1113        EmitCode::emit_code(self)
1114    }
1115}
1116
1117impl ToCodeLiteral for std::net::Ipv6Addr {
1118    fn to_code_literal(&self) -> TokenStream {
1119        EmitCode::emit_code(self)
1120    }
1121}
1122
1123impl ToCodeLiteral for std::path::PathBuf {
1124    fn to_code_literal(&self) -> TokenStream {
1125        EmitCode::emit_code(self)
1126    }
1127}
1128
1129impl ToCodeLiteral for std::time::Duration {
1130    fn to_code_literal(&self) -> TokenStream {
1131        EmitCode::emit_code(self)
1132    }
1133}
1134
1135#[cfg(feature = "serde_json")]
1136impl ToCodeLiteral for serde_json::Value {
1137    fn to_code_literal(&self) -> TokenStream {
1138        EmitCode::emit_code(self)
1139    }
1140}
1141
1142#[cfg(feature = "url")]
1143impl ToCodeLiteral for url::Url {
1144    fn to_code_literal(&self) -> TokenStream {
1145        EmitCode::emit_code(self)
1146    }
1147}
1148
1149#[cfg(feature = "url")]
1150impl ToCodeLiteral for url::SyntaxViolation {
1151    fn to_code_literal(&self) -> TokenStream {
1152        use url::SyntaxViolation::*;
1153        match self {
1154            Backslash => quote::quote! { url::SyntaxViolation::Backslash },
1155            C0SpaceIgnored => quote::quote! { url::SyntaxViolation::C0SpaceIgnored },
1156            EmbeddedCredentials => quote::quote! { url::SyntaxViolation::EmbeddedCredentials },
1157            ExpectedDoubleSlash => quote::quote! { url::SyntaxViolation::ExpectedDoubleSlash },
1158            ExpectedFileDoubleSlash => {
1159                quote::quote! { url::SyntaxViolation::ExpectedFileDoubleSlash }
1160            }
1161            FileWithHostAndWindowsDrive => {
1162                quote::quote! { url::SyntaxViolation::FileWithHostAndWindowsDrive }
1163            }
1164            NonUrlCodePoint => quote::quote! { url::SyntaxViolation::NonUrlCodePoint },
1165            NullInFragment => quote::quote! { url::SyntaxViolation::NullInFragment },
1166            PercentDecode => quote::quote! { url::SyntaxViolation::PercentDecode },
1167            TabOrNewlineIgnored => quote::quote! { url::SyntaxViolation::TabOrNewlineIgnored },
1168            UnencodedAtSign => quote::quote! { url::SyntaxViolation::UnencodedAtSign },
1169            _ => unreachable!("unknown SyntaxViolation variant"),
1170        }
1171    }
1172}
1173
1174#[cfg(feature = "uuid")]
1175impl ToCodeLiteral for uuid::Uuid {
1176    fn to_code_literal(&self) -> TokenStream {
1177        EmitCode::emit_code(self)
1178    }
1179}
1180
1181#[cfg(feature = "chrono")]
1182impl ToCodeLiteral for chrono::DateTime<chrono::Utc> {
1183    fn to_code_literal(&self) -> TokenStream {
1184        EmitCode::emit_code(self)
1185    }
1186}
1187
1188#[cfg(feature = "chrono")]
1189impl ToCodeLiteral for chrono::NaiveDateTime {
1190    fn to_code_literal(&self) -> TokenStream {
1191        EmitCode::emit_code(self)
1192    }
1193}
1194
1195#[cfg(feature = "chrono")]
1196impl ToCodeLiteral for chrono::DateTime<chrono::FixedOffset> {
1197    fn to_code_literal(&self) -> TokenStream {
1198        let s = self.to_rfc3339();
1199        quote::quote! {
1200            chrono::DateTime::parse_from_rfc3339(#s)
1201                .expect("valid RFC3339 datetime")
1202        }
1203    }
1204
1205    fn type_tokens() -> TokenStream {
1206        quote::quote! { chrono::DateTime<chrono::FixedOffset> }
1207    }
1208}
1209
1210#[cfg(feature = "chrono")]
1211impl ToCodeLiteral for chrono::TimeDelta {
1212    fn to_code_literal(&self) -> TokenStream {
1213        let secs = self.num_seconds();
1214        let sub_nanos = self.subsec_nanos();
1215        if sub_nanos == 0 {
1216            quote::quote! {
1217                chrono::TimeDelta::try_seconds(#secs).expect("valid seconds")
1218            }
1219        } else {
1220            // Build as whole-second base + nanosecond remainder
1221            quote::quote! {
1222                chrono::TimeDelta::try_seconds(#secs).expect("valid seconds")
1223                    + chrono::TimeDelta::nanoseconds(#sub_nanos as i64)
1224            }
1225        }
1226    }
1227
1228    fn type_tokens() -> TokenStream {
1229        quote::quote! { chrono::TimeDelta }
1230    }
1231}
1232
1233#[cfg(feature = "time")]
1234impl ToCodeLiteral for time::OffsetDateTime {
1235    fn to_code_literal(&self) -> TokenStream {
1236        EmitCode::emit_code(self)
1237    }
1238}
1239
1240#[cfg(feature = "time")]
1241impl ToCodeLiteral for time::PrimitiveDateTime {
1242    fn to_code_literal(&self) -> TokenStream {
1243        EmitCode::emit_code(self)
1244    }
1245}
1246
1247#[cfg(feature = "time")]
1248impl ToCodeLiteral for time::Time {
1249    fn to_code_literal(&self) -> TokenStream {
1250        EmitCode::emit_code(self)
1251    }
1252}
1253
1254#[cfg(feature = "jiff")]
1255impl ToCodeLiteral for jiff::Timestamp {
1256    fn to_code_literal(&self) -> TokenStream {
1257        EmitCode::emit_code(self)
1258    }
1259}
1260
1261#[cfg(feature = "jiff")]
1262impl ToCodeLiteral for jiff::Zoned {
1263    fn to_code_literal(&self) -> TokenStream {
1264        EmitCode::emit_code(self)
1265    }
1266}
1267
1268#[cfg(feature = "jiff")]
1269impl ToCodeLiteral for jiff::civil::DateTime {
1270    fn to_code_literal(&self) -> TokenStream {
1271        EmitCode::emit_code(self)
1272    }
1273}
1274
1275#[cfg(feature = "reqwest")]
1276impl ToCodeLiteral for reqwest::StatusCode {
1277    fn to_code_literal(&self) -> TokenStream {
1278        EmitCode::emit_code(self)
1279    }
1280}
1281
1282// ── Atomic types ─────────────────────────────────────────────────────────────
1283
1284/// Generate `ToCodeLiteral` for an atomic type by loading the current value
1285/// and emitting `::std::sync::atomic::Atomic*::new(value)`.
1286macro_rules! impl_atomic_to_code_literal {
1287    ($($atomic:ident => $prim:ty),+ $(,)?) => {
1288        $(
1289            impl ToCodeLiteral for ::std::sync::atomic::$atomic {
1290                fn to_code_literal(&self) -> TokenStream {
1291                    use ::std::sync::atomic::Ordering;
1292                    let val = self.load(Ordering::SeqCst);
1293                    let val_lit = <$prim as ToCodeLiteral>::to_code_literal(&val);
1294                    let ty: TokenStream =
1295                        concat!("::std::sync::atomic::", stringify!($atomic))
1296                            .parse()
1297                            .expect("valid atomic type path");
1298                    quote::quote! { #ty::new(#val_lit) }
1299                }
1300
1301                fn type_tokens() -> TokenStream {
1302                    concat!("::std::sync::atomic::", stringify!($atomic))
1303                        .parse()
1304                        .expect("valid atomic type path")
1305                }
1306            }
1307        )+
1308    };
1309}
1310
1311impl_atomic_to_code_literal!(
1312    AtomicBool => bool,
1313    AtomicI8 => i8,
1314    AtomicI16 => i16,
1315    AtomicI32 => i32,
1316    AtomicI64 => i64,
1317    AtomicIsize => isize,
1318    AtomicU8 => u8,
1319    AtomicU16 => u16,
1320    AtomicU32 => u32,
1321    AtomicU64 => u64,
1322    AtomicUsize => usize,
1323);