Skip to content

feat: replace #[schema] with vendored JsonSchema derive#30

Merged
Arthurdw merged 4 commits intomainfrom
feat/auto-schema-derive
Apr 3, 2026
Merged

feat: replace #[schema] with vendored JsonSchema derive#30
Arthurdw merged 4 commits intomainfrom
feat/auto-schema-derive

Conversation

@Arthurdw
Copy link
Copy Markdown
Owner

@Arthurdw Arthurdw commented Apr 3, 2026

Vendor schemars_derive into rovo-macros with the default crate path changed to ::rovo::schemars. This makes #[derive(JsonSchema)] work out of the box when imported from rovo — no helper attributes or special import patterns required.

Remove the #[schema] attribute macro as it is no longer needed.

Closes #28

Summary by CodeRabbit

  • Breaking Changes

    • The old attribute-style schema macro was removed from the public API—migrate to using #[derive(JsonSchema)].
  • New Features

    • Added a JsonSchema derive macro and a public schemars module that exposes the derive.
  • Tests

    • Test suite updated to exercise the derive-based workflow and explicit schemars crate path.

Vendor schemars_derive into rovo-macros with the default crate path
changed to ::rovo::schemars. This makes #[derive(JsonSchema)] work
out of the box when imported from rovo — no helper attributes or
special import patterns required.

Remove the #[schema] attribute macro as it is no longer needed.
@Arthurdw Arthurdw self-assigned this Apr 3, 2026
@Arthurdw Arthurdw added the enhancement New feature or request label Apr 3, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 3, 2026

Codecov Report

❌ Patch coverage is 94.73684% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.43%. Comparing base (9e36d9d) to head (3ee7af7).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
rovo-macros/src/lib.rs 94.73% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #30      +/-   ##
==========================================
- Coverage   92.44%   92.43%   -0.01%     
==========================================
  Files          17       17              
  Lines        5519     5529      +10     
==========================================
+ Hits         5102     5111       +9     
- Misses        417      418       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 45e432b2-fc38-4de4-b452-63b85ddf9548

📥 Commits

Reviewing files that changed from the base of the PR and between 9b52172 and 3ee7af7.

📒 Files selected for processing (2)
  • rovo-macros/src/lib.rs
  • tests/schema_macro.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • rovo-macros/src/lib.rs
  • tests/schema_macro.rs

📝 Walkthrough

Walkthrough

Replaced the #[schema] attribute macro with a #[derive(JsonSchema)] derive implementation, added an internal schemars re-export, removed the public schema macro, and updated examples/tests to use the new derive and re-exported schemars paths.

Changes

Cohort / File(s) Summary
Macro Implementation
rovo-macros/src/lib.rs
Removed the #[schema] attribute macro; added #[proc_macro_derive(JsonSchema, attributes(...))] derive that renames the input to a hidden __rovo_<Name> type, conditionally injects #[schemars(crate = "...")] if absent, and implements JsonSchema for the original type by delegating to the hidden type while rewriting schema identifiers.
Public API Restructuring
src/lib.rs
Removed public export of schema; introduced #[doc(hidden)] pub use ::schemars as __schemars; and pub mod schemars { pub use ::schemars::*; pub use rovo_macros::JsonSchema; } to expose a schemars re-export and macro-compatible JsonSchema.
Examples & Tests
examples/hello_world/src/main.rs, tests/schema_macro.rs
Stripped #[schema] usages and imports, switched to #[derive(JsonSchema)] in tests and examples, updated tests to assert #[schemars(crate = "::rovo::schemars")] behavior and renamed/added test types (e.g., ExplicitCratePath, Wrapper).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

release:patch

Poem

🐇 I nibbled attributes away by night,
I stitched a derive in morning light,
Hidden types hop where schemas hide,
Paths now clear — a joyful stride! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: replacing the #[schema] attribute macro with a vendored JsonSchema derive macro implementation.
Linked Issues check ✅ Passed The PR successfully addresses issue #28 by making JsonSchema derive use ::rovo::schemars instead of top-level schemars, eliminating unresolved-crate errors for users importing via rovo.
Out of Scope Changes check ✅ Passed All changes are directly related to replacing #[schema] with JsonSchema derive and fixing the schemars path resolution; no out-of-scope modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/auto-schema-derive

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Use a proxy struct approach: our JsonSchema derive creates a hidden copy
of the type, applies schemars' original derive with the crate path set
to ::rovo::schemars, then generates a thin delegation impl for the
original type.

This makes #[derive(JsonSchema)] work out of the box when imported from
rovo — no helper attributes or special import patterns required.

Remove the #[schema] attribute macro as it is no longer needed.
@Arthurdw Arthurdw marked this pull request as draft April 3, 2026 11:59
…to feat/auto-schema-derive

# Conflicts:
#	rovo-macros/src/lib.rs
#	src/lib.rs
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
rovo-macros/src/json_schema/attr/doc.rs (1)

1-5: Unused imports present.

format_ident, quote_spanned, and Token are imported but not used in this file. While the parent module suppresses unused import warnings, cleaning these up would improve clarity.

♻️ Suggested cleanup
 use proc_macro2::TokenStream;
 use quote::TokenStreamExt;
-use quote::{format_ident, quote, quote_spanned};
+use quote::quote;
 use syn::Attribute;
-use syn::{parse_quote, Token};
+use syn::parse_quote;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rovo-macros/src/json_schema/attr/doc.rs` around lines 1 - 5, Remove the
unused imports to clean up the module: delete format_ident, quote_spanned, and
Token from the use statements (they are currently imported in the top of the
file alongside TokenStream, TokenStreamExt, quote, and parse_quote) so only
actually used symbols remain; update the use lines in doc.rs to import only
proc_macro2::TokenStream, quote::TokenStreamExt, quote, and syn::{Attribute,
parse_quote} (or equivalent) to eliminate the unused-imports while preserving
existing functionality.
rovo-macros/src/lib.rs (1)

19-27: Broad lint suppressions for vendored module.

The lint suppressions are reasonable for vendored code from schemars_derive. Consider adding a brief comment explaining these are suppressed because the code is vendored and minimally modified.

📝 Suggested documentation
+// Vendored from schemars_derive - lint warnings suppressed to minimize divergence from upstream.
 #[allow(
     clippy::all,
     clippy::nursery,
     clippy::pedantic,
     missing_docs,
     rust_2018_idioms,
     unused_imports
 )]
 mod json_schema;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rovo-macros/src/lib.rs` around lines 19 - 27, Add a brief explanatory comment
above the existing #[allow(...)] attribute for the vendored mod json_schema
explaining that these broad lint suppressions are intentional because the code
is vendored from schemars_derive and only minimally modified; update the comment
to reference the vendored source (schemars_derive) and state that clippy/idiom
warnings are suppressed to avoid noisy lints, leaving the #[allow(...)]
attribute and mod json_schema unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rovo-macros/src/json_schema/attr/schemars_to_serde.rs`:
- Around line 76-87: The code currently keys override tracking by only the
top-level meta name (keyword) so a schemars attribute like
#[schemars(rename(serialize = "..."))] will suppress an unrelated serde nested
half (rename(deserialize=...)). Change the keying to include nested subkeys for
paired items (e.g., "rename.serialize" vs "rename.deserialize") instead of just
"rename": when iterating get_meta_items(attrs, "schemars", ctxt) inside the
block that builds serde_meta and schemars_meta_names, detect nested meta-list
entries (rename/rename_all with named sub-entries) and derive a more specific
identifier (use the top-level ident plus the nested ident like
"rename.serialize" or "rename_all.case") to store in schemars_meta_names; update
the same logic in the other similar block (lines ~94-103) so that only the exact
nested subkey suppresses the serde item, allowing the untouched half (e.g.,
deserialize) to be preserved and merged.

---

Nitpick comments:
In `@rovo-macros/src/json_schema/attr/doc.rs`:
- Around line 1-5: Remove the unused imports to clean up the module: delete
format_ident, quote_spanned, and Token from the use statements (they are
currently imported in the top of the file alongside TokenStream, TokenStreamExt,
quote, and parse_quote) so only actually used symbols remain; update the use
lines in doc.rs to import only proc_macro2::TokenStream, quote::TokenStreamExt,
quote, and syn::{Attribute, parse_quote} (or equivalent) to eliminate the
unused-imports while preserving existing functionality.

In `@rovo-macros/src/lib.rs`:
- Around line 19-27: Add a brief explanatory comment above the existing
#[allow(...)] attribute for the vendored mod json_schema explaining that these
broad lint suppressions are intentional because the code is vendored from
schemars_derive and only minimally modified; update the comment to reference the
vendored source (schemars_derive) and state that clippy/idiom warnings are
suppressed to avoid noisy lints, leaving the #[allow(...)] attribute and mod
json_schema unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ccd311d-b9e6-41d6-ad02-788277d26a77

📥 Commits

Reviewing files that changed from the base of the PR and between 9e36d9d and 5e01c7e.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • examples/hello_world/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • examples/hello_world/src/main.rs
  • rovo-macros/Cargo.toml
  • rovo-macros/src/json_schema/ast/from_serde.rs
  • rovo-macros/src/json_schema/ast/mod.rs
  • rovo-macros/src/json_schema/attr/doc.rs
  • rovo-macros/src/json_schema/attr/mod.rs
  • rovo-macros/src/json_schema/attr/parse_meta.rs
  • rovo-macros/src/json_schema/attr/schemars_to_serde.rs
  • rovo-macros/src/json_schema/attr/validation.rs
  • rovo-macros/src/json_schema/idents.rs
  • rovo-macros/src/json_schema/mod.rs
  • rovo-macros/src/json_schema/schema_exprs.rs
  • rovo-macros/src/lib.rs
  • src/lib.rs
  • tests/schema_macro.rs

@Arthurdw Arthurdw marked this pull request as ready for review April 3, 2026 12:12
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
rovo-macros/src/lib.rs (1)

495-513: Consider using syn's meta parsing for more robust attribute detection.

The current token iteration approach works for the standard #[schemars(crate = "...")] syntax, but it's slightly fragile—crate is a reserved keyword, and flat token iteration may not handle all edge cases (e.g., nested attribute structures).

Using syn::Attribute::parse_nested_meta would be more robust:

let has_crate_attr = hidden.attrs.iter().any(|attr| {
    if !attr.path().is_ident("schemars") {
        return false;
    }
    let mut found = false;
    let _ = attr.parse_nested_meta(|meta| {
        if meta.path.is_ident("crate") {
            found = true;
        }
        Ok(())
    });
    found
});

That said, the current implementation should work fine for typical usage patterns.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rovo-macros/src/lib.rs` around lines 495 - 513, The current has_crate_attr
detection (iterating hidden.attrs and inspecting attr.meta tokens) is brittle;
replace it by using syn's nested meta parsing: keep the
attr.path().is_ident("schemars") check, then call attr.parse_nested_meta(|meta|
{ if meta.path.is_ident("crate") { found = true } Ok(()) }) (or equivalent using
Attribute::parse_nested_meta) to set found and return it as has_crate_attr, and
leave crate_attr construction (quote! { #[schemars(crate = "::rovo::schemars")]
}) unchanged; update the has_crate_attr variable initialization to use this
parse_nested_meta-based detection for robust attribute handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rovo-macros/src/lib.rs`:
- Around line 515-534: The delegation impl is missing the JsonSchema bound for
generic parameters, causing compilation when the hidden helper type requires
bounds; update the generated where clause used with
impl_generics/ty_generics/where_clause so it includes a predicate that the
hidden helper type implements ::rovo::schemars::JsonSchema (e.g. add a
where-clause entry like `#hidden_name `#ty_generics` :
::rovo::schemars::JsonSchema` or merge that predicate into the existing
where_clause) before emitting the impl for `::rovo::schemars::JsonSchema` for
`#original_name`, ensuring the delegation calls to `<#hidden_name `#ty_generics`
as ::rovo::schemars::JsonSchema>::...` compile for generic types.

---

Nitpick comments:
In `@rovo-macros/src/lib.rs`:
- Around line 495-513: The current has_crate_attr detection (iterating
hidden.attrs and inspecting attr.meta tokens) is brittle; replace it by using
syn's nested meta parsing: keep the attr.path().is_ident("schemars") check, then
call attr.parse_nested_meta(|meta| { if meta.path.is_ident("crate") { found =
true } Ok(()) }) (or equivalent using Attribute::parse_nested_meta) to set found
and return it as has_crate_attr, and leave crate_attr construction (quote! {
#[schemars(crate = "::rovo::schemars")] }) unchanged; update the has_crate_attr
variable initialization to use this parse_nested_meta-based detection for robust
attribute handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0b9f595f-46f0-42bd-9c5b-0b5784dabee2

📥 Commits

Reviewing files that changed from the base of the PR and between 5e01c7e and 9b52172.

📒 Files selected for processing (2)
  • rovo-macros/src/lib.rs
  • src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib.rs

The proxy derive's delegation impl was missing a where clause requiring
the hidden type to implement JsonSchema. This caused compile errors for
generic types like `struct Wrapper<T> { inner: T }`.

Add a test covering generic types.
@Arthurdw Arthurdw merged commit 07020ea into main Apr 3, 2026
10 checks passed
@Arthurdw Arthurdw deleted the feat/auto-schema-derive branch April 3, 2026 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use own re-export of schemars in JsonSchema macro

1 participant