Repository: GREsau/schemars
Pull request #80 (2021-04-05 17:48:23)

Support for #[schemars(crate = "...")]
Supersedes #55 


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e1c60c5..bcbf6d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,11 @@
 # Changelog
 
 ## [0.8.3] - **In-dev**
+### Added:
+- Support for `#[schemars(crate = "...")]` attribute to allow deriving JsonSchema when the schemars crate is aliased to a different name (https://github.com/GREsau/schemars/pull/55 / https://github.com/GREsau/schemars/pull/80)
+
 ### Fixed:
-- Fix deriving JsonSchema on types defined inside macros (https://github.com/GREsau/schemars/issues/66 / https://github.com/GREsau/schemars/pull/79)
+- Fix deriving JsonSchema on types defined inside macros (https://github.com/GREsau/schemars/issues/59 / https://github.com/GREsau/schemars/issues/66 / https://github.com/GREsau/schemars/pull/79)
 
 ## [0.8.2] - 2021-03-27
 ### Added:
diff --git a/docs/1.1-attributes.md b/docs/1.1-attributes.md
index 3cc38c4..50bc753 100644
--- a/docs/1.1-attributes.md
+++ b/docs/1.1-attributes.md
@@ -182,6 +182,16 @@ Set on a container, variant or field to include the result of the given function
 
 Set the Rust built-in [`deprecated`](https://doc.rust-lang.org/edition-guide/rust-2018/the-compiler/an-attribute-for-deprecation.html) attribute on a struct, enum, field or variant to set the generated schema's `deprecated` keyword to `true`.
 
+<!--
+TODO Uncomment for 0.8.3
+<h3 id="crate">
+
+`#[schemars(crate = "other_crate::schemars")]`
+</h3>
+
+Set the path to the schemars crate instance the generated code should depend on. This is mostly useful for other crates that depend on schemars in their macros.
+-->
+
 <h3 id="doc">
 
 Doc Comments (`#[doc = "..."]`)
diff --git a/schemars_derive/src/attr/mod.rs b/schemars_derive/src/attr/mod.rs
index 65667d9..04c89fe 100644
--- a/schemars_derive/src/attr/mod.rs
+++ b/schemars_derive/src/attr/mod.rs
@@ -11,6 +11,10 @@ use syn::Meta::{List, NameValue};
 use syn::MetaNameValue;
 use syn::NestedMeta::{Lit, Meta};
 
+// FIXME using the same struct for containers+variants+fields means that
+//  with/schema_with are accepted (but ignored) on containers, and
+//  repr/crate_name are accepted (but ignored) on variants and fields etc.
+
 #[derive(Debug, Default)]
 pub struct Attrs {
     pub with: Option<WithAttr>,
@@ -19,6 +23,7 @@ pub struct Attrs {
     pub deprecated: bool,
     pub examples: Vec<syn::Path>,
     pub repr: Option<syn::Type>,
+    pub crate_name: Option<syn::Path>,
 }
 
 #[derive(Debug)]
@@ -123,6 +128,16 @@ impl Attrs {
                     }
                 }
 
+                Meta(NameValue(m)) if m.path.is_ident("crate") => {
+                    if let Ok(p) = parse_lit_into_path(errors, attr_type, "crate", &m.lit) {
+                        if self.crate_name.is_some() {
+                            duplicate_error(m)
+                        } else {
+                            self.crate_name = Some(p)
+                        }
+                    }
+                }
+
                 _ if ignore_errors => {}
 
                 Meta(meta_item) => {
diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs
index c81eb40..7b140a5 100644
--- a/schemars_derive/src/lib.rs
+++ b/schemars_derive/src/lib.rs
@@ -13,6 +13,7 @@ mod schema_exprs;
 
 use ast::*;
 use proc_macro2::TokenStream;
+use syn::spanned::Spanned;
 
 #[proc_macro_derive(JsonSchema, attributes(schemars, serde))]
 pub fn derive_json_schema_wrapper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
@@ -39,6 +40,11 @@ fn derive_json_schema(
     attr::process_serde_attrs(&mut input)?;
 
     let cont = Container::from_ast(&input)?;
+    let crate_alias = cont.attrs.crate_name.as_ref().map(|path| {
+        quote_spanned! {path.span()=>
+            use #path as schemars;
+        }
+    });
 
     let type_name = &cont.ident;
     let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
@@ -47,6 +53,7 @@ fn derive_json_schema(
         let (ty, type_def) = schema_exprs::type_for_schema(transparent_field, 0);
         return Ok(quote! {
             const _: () = {
+                #crate_alias
                 #type_def
 
                 #[automatically_derived]
@@ -121,16 +128,20 @@ fn derive_json_schema(
     };
 
     Ok(quote! {
-        #[automatically_derived]
-        #[allow(unused_braces)]
-        impl #impl_generics schemars::JsonSchema for #type_name #ty_generics #where_clause {
-            fn schema_name() -> std::string::String {
-                #schema_name
-            }
-
-            fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
-                #schema_expr
-            }
+        const _: () = {
+            #crate_alias
+
+            #[automatically_derived]
+            #[allow(unused_braces)]
+            impl #impl_generics schemars::JsonSchema for #type_name #ty_generics #where_clause {
+                fn schema_name() -> std::string::String {
+                    #schema_name
+                }
+
+                fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+                    #schema_expr
+                }
+            };
         };
     })
 }


Test patch:
diff --git a/schemars/tests/crate_alias.rs b/schemars/tests/crate_alias.rs
new file mode 100644
index 0000000..5f8d83c
--- /dev/null
+++ b/schemars/tests/crate_alias.rs
@@ -0,0 +1,19 @@
+mod util;
+use ::schemars as not_schemars;
+use util::*;
+
+#[allow(unused_imports)]
+use std as schemars;
+
+#[derive(Debug, not_schemars::JsonSchema)]
+#[schemars(crate = "not_schemars")]
+pub struct Struct {
+    /// This is a document
+    foo: i32,
+    bar: bool,
+}
+
+#[test]
+fn test_crate_alias() -> TestResult {
+    test_default_generated_schema::<Struct>("crate_alias")
+}
diff --git a/schemars/tests/expected/crate_alias.json b/schemars/tests/expected/crate_alias.json
new file mode 100644
index 0000000..66bf749
--- /dev/null
+++ b/schemars/tests/expected/crate_alias.json
@@ -0,0 +1,19 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "Struct",
+  "type": "object",
+  "required": [
+    "bar",
+    "foo"
+  ],
+  "properties": {
+    "foo": {
+      "description": "This is a document",
+      "type": "integer",
+      "format": "int32"
+    },
+    "bar": {
+      "type": "boolean"
+    }
+  }
+}
\ No newline at end of file