Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(derive): crate path attribute #55

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/1.1-attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ TABLE OF CONTENTS
- [`title` / `description`](#title-description)
- [`example`](#example)
- [`deprecated`](#deprecated)
- [`crate`](#crate)
- [Doc Comments (`doc`)](#doc)
</details>

Expand Down Expand Up @@ -182,6 +183,13 @@ 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`.

<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 = "..."]`)
Expand Down
12 changes: 12 additions & 0 deletions schemars/tests/derive_crate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use ::schemars as not_schemars;

#[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,
}
11 changes: 11 additions & 0 deletions schemars_derive/src/attr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Attrs {
pub title: Option<String>,
pub description: Option<String>,
pub deprecated: bool,
pub crate_name: Option<syn::Path>,
pub examples: Vec<syn::Path>,
}

Expand Down Expand Up @@ -118,6 +119,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) => {
Expand Down
49 changes: 30 additions & 19 deletions schemars_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ pub fn derive_json_schema_wrapper(input: proc_macro::TokenStream) -> proc_macro:
}

fn derive_json_schema(mut input: syn::DeriveInput) -> TokenStream {
add_trait_bounds(&mut input.generics);

if let Err(e) = attr::process_serde_attrs(&mut input) {
return compile_error(&e);
}
Expand All @@ -32,40 +30,51 @@ fn derive_json_schema(mut input: syn::DeriveInput) -> TokenStream {
Err(e) => return compile_error(&e),
};

let default_crate_name: syn::Path = parse_quote!(schemars);
let crate_name = cont
.attrs
.crate_name
.as_ref()
.unwrap_or(&default_crate_name);

let mut gen = cont.generics.clone();

add_trait_bounds(&crate_name, &mut gen);

let type_name = &cont.ident;
let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
let (impl_generics, ty_generics, where_clause) = gen.split_for_impl();

if let Some(transparent_field) = cont.transparent_field() {
let (ty, type_def) = schema_exprs::type_for_schema(transparent_field, 0);
let (ty, type_def) = schema_exprs::type_for_schema(crate_name, transparent_field, 0);
return quote! {
#[automatically_derived]
impl #impl_generics schemars::JsonSchema for #type_name #ty_generics #where_clause {
impl #impl_generics #crate_name::JsonSchema for #type_name #ty_generics #where_clause {
#type_def

fn is_referenceable() -> bool {
<#ty as schemars::JsonSchema>::is_referenceable()
<#ty as #crate_name::JsonSchema>::is_referenceable()
}

fn schema_name() -> std::string::String {
<#ty as schemars::JsonSchema>::schema_name()
<#ty as #crate_name::JsonSchema>::schema_name()
}

fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
<#ty as schemars::JsonSchema>::json_schema(gen)
fn json_schema(gen: &mut #crate_name::gen::SchemaGenerator) -> #crate_name::schema::Schema {
<#ty as #crate_name::JsonSchema>::json_schema(gen)
}

fn json_schema_for_flatten(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
<#ty as schemars::JsonSchema>::json_schema_for_flatten(gen)
fn json_schema_for_flatten(gen: &mut #crate_name::gen::SchemaGenerator) -> #crate_name::schema::Schema {
<#ty as #crate_name::JsonSchema>::json_schema_for_flatten(gen)
}

fn add_schema_as_property(
gen: &mut schemars::gen::SchemaGenerator,
parent: &mut schemars::schema::SchemaObject,
gen: &mut #crate_name::gen::SchemaGenerator,
parent: &mut #crate_name::schema::SchemaObject,
name: String,
metadata: Option<schemars::schema::Metadata>,
metadata: Option<#crate_name::schema::Metadata>,
required: bool,
) {
<#ty as schemars::JsonSchema>::add_schema_as_property(gen, parent, name, metadata, required)
<#ty as #crate_name::JsonSchema>::add_schema_as_property(gen, parent, name, metadata, required)
}
};
};
Expand Down Expand Up @@ -109,22 +118,24 @@ fn derive_json_schema(mut input: syn::DeriveInput) -> TokenStream {
quote! {
#[automatically_derived]
#[allow(unused_braces)]
impl #impl_generics schemars::JsonSchema for #type_name #ty_generics #where_clause {
impl #impl_generics #crate_name::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 {
fn json_schema(gen: &mut #crate_name::gen::SchemaGenerator) -> #crate_name::schema::Schema {
#schema_expr
}
};
}
}

fn add_trait_bounds(generics: &mut syn::Generics) {
fn add_trait_bounds(crate_name: &syn::Path, generics: &mut syn::Generics) {
for param in &mut generics.params {
if let syn::GenericParam::Type(ref mut type_param) = *param {
type_param.bounds.push(parse_quote!(schemars::JsonSchema));
type_param
.bounds
.push(parse_quote!(#crate_name::JsonSchema));
}
}
}
Expand Down
14 changes: 10 additions & 4 deletions schemars_derive/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@ pub struct SchemaMetadata<'a> {
pub deprecated: bool,
pub read_only: bool,
pub write_only: bool,
pub crate_name: &'a syn::Path,
pub examples: &'a [syn::Path],
pub default: Option<TokenStream>,
}

impl ToTokens for SchemaMetadata<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let setters = self.make_setters();
let crate_name = self.crate_name;
if setters.is_empty() {
tokens.append(Ident::new("None", Span::call_site()))
} else {
tokens.extend(quote! {
Some({
let mut metadata = schemars::schema::Metadata::default();
let mut metadata = #crate_name::schema::Metadata::default();
#(#setters)*
metadata
})
Expand All @@ -32,12 +34,15 @@ impl ToTokens for SchemaMetadata<'_> {
}

impl<'a> SchemaMetadata<'a> {
pub fn from_attrs(attrs: &'a Attrs) -> Self {
// Crate name is separate, because attrs could be for a variant
// instead of a container, and crate is only applicable to containers.
pub fn from_attrs(crate_name: &'a syn::Path, attrs: &'a Attrs) -> Self {
SchemaMetadata {
title: attrs.title.as_ref().and_then(none_if_empty),
description: attrs.description.as_ref().and_then(none_if_empty),
deprecated: attrs.deprecated,
examples: &attrs.examples,
crate_name,
read_only: false,
write_only: false,
default: None,
Expand All @@ -55,6 +60,7 @@ impl<'a> SchemaMetadata<'a> {

fn make_setters(&self) -> Vec<TokenStream> {
let mut setters = Vec::<TokenStream>::new();
let crate_name = self.crate_name;

if let Some(title) = &self.title {
setters.push(quote! {
Expand Down Expand Up @@ -87,7 +93,7 @@ impl<'a> SchemaMetadata<'a> {
if !self.examples.is_empty() {
let examples = self.examples.iter().map(|eg| {
quote! {
schemars::_serde_json::value::to_value(#eg())
#crate_name::_serde_json::value::to_value(#eg())
}
});
setters.push(quote! {
Expand All @@ -97,7 +103,7 @@ impl<'a> SchemaMetadata<'a> {

if let Some(default) = &self.default {
setters.push(quote! {
metadata.default = #default.and_then(|d| schemars::_serde_json::value::to_value(d).ok());
metadata.default = #default.and_then(|d| #crate_name::_serde_json::value::to_value(d).ok());
});
}

Expand Down
Loading