Deserialize directly from mlua::Value via mlua::serde::Deserializer
Removes the intermediate serde_json::Value hop — mlua exposes a public
serde::Deserializer that serde_ignored can wrap directly.

https://claude.ai/code/session_015tdp3ahAmmdE69yCsV7J8b
change
commit 6eccf183dfbfede518cd138103dc713c9776dbb6
author Claude <noreply@anthropic.com>
date
parent fb4569eb
diff --git a/quire-core/src/fennel.rs b/quire-core/src/fennel.rs
index ccce305..1b5a4f1 100644
--- a/quire-core/src/fennel.rs
+++ b/quire-core/src/fennel.rs
@@ -1,7 +1,7 @@
 use std::path::Path;
 
 use miette::{Diagnostic, NamedSource, SourceOffset};
-use mlua::{Lua, LuaSerdeExt};
+use mlua::Lua;
 use thiserror::Error;
 
 const FENNEL_LUA: &str = include_str!("../vendor/fennel.lua");
@@ -163,22 +163,14 @@ impl Fennel {
     {
         let result = self.eval_raw(source, name, |_| Ok(()))?;
 
-        // Convert to a generic JSON value so serde_ignored can walk the tree
-        // and detect fields that no known struct key consumes.
-        let json_val: serde_json::Value = self.lua.from_value(result).map_err(|e| {
-            let message = format!("{name}: {e}");
-            FennelError::TypeMismatch {
-                message,
-                source: Box::new(e),
-            }
-        })?;
+        let de = mlua::serde::Deserializer::new(result);
 
-        serde_ignored::deserialize(&json_val, |path| {
+        serde_ignored::deserialize(de, |path| {
             tracing::warn!(config = %name, field = %path, "unknown config field ignored");
         })
         .map_err(|e| FennelError::TypeMismatch {
             message: format!("{name}: {e}"),
-            source: Box::new(mlua::Error::external(e)),
+            source: Box::new(e),
         })
     }