`derive/src/` — the `#[derive(Tool)]` crate [feature: derive]
The companion crate loopctl-derive (a Cargo workspace member): three files that turn a Deserialize struct into a full Tool impl.
Files
lib.rs— the proc-macro entry point:#[proc_macro_derive(Tool, attributes(tool))], re-exported by loopctl under thederivefeature.attr.rs— attribute parsing: container keys (name,description,read_only,concurrency_safe,system_prompt,handler,allow_extra) and field keys (name,description,skip,default); serde-rename agreement checking; the snake-case/acronym-aware name derivation; later-attribute-wins.expand.rs— code generation: theimpl Tool(name/description literals, statically emittedjson!schema,callthat clones theToolContext, deserializes, and dispatches to the handler — defaultrun), the Rust-type→JSON-Schema map, theOption/serde-default required-list rules, and every spanned error case.
Behavior notes
- All diagnostics are spanned
syn::Errors → normal compile errors at the offending token; the macro itself never panics. - Generated code uses
loopctl::__private::serde_json— downstream crates need no direct serde_json dependency. - The UI test suite (
derive/tests/ui/with trybuild goldens) pins every error message.
Deep dive: The derive macro.