Repository: GREsau/schemars
Pull request #239 (2023-08-27 19:45:37)

Support types with const generics
Supersedes #179


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 63cd2f2..061a4a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 ### Added:
 
 - Implement `JsonSchema` for `semver::Version` (https://github.com/GREsau/schemars/pull/195 / https://github.com/GREsau/schemars/pull/238)
+- Include const generics in generated schema names (https://github.com/GREsau/schemars/pull/179 / https://github.com/GREsau/schemars/pull/239)
 
 ### Changed:
 
diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs
index f8173f4..e3b3a4f 100644
--- a/schemars_derive/src/lib.rs
+++ b/schemars_derive/src/lib.rs
@@ -95,27 +95,31 @@ fn derive_json_schema(
 
     // FIXME improve handling of generic type params which may not implement JsonSchema
     let type_params: Vec<_> = cont.generics.type_params().map(|ty| &ty.ident).collect();
-    let schema_name =
-        if type_params.is_empty() || (cont.attrs.is_renamed && !schema_base_name.contains('{')) {
-            quote! {
-                #schema_base_name.to_owned()
-            }
-        } else if cont.attrs.is_renamed {
-            let mut schema_name_fmt = schema_base_name;
-            for tp in &type_params {
-                schema_name_fmt.push_str(&format!("{{{}:.0}}", tp));
-            }
-            quote! {
-                format!(#schema_name_fmt #(,#type_params=#type_params::schema_name())*)
-            }
-        } else {
-            let mut schema_name_fmt = schema_base_name;
-            schema_name_fmt.push_str("_for_{}");
-            schema_name_fmt.push_str(&"_and_{}".repeat(type_params.len() - 1));
-            quote! {
-                format!(#schema_name_fmt #(,#type_params::schema_name())*)
-            }
-        };
+    let const_params: Vec<_> = cont.generics.const_params().map(|c| &c.ident).collect();
+    let params: Vec<_> = type_params.iter().chain(const_params.iter()).collect();
+
+    let schema_name = if params.is_empty()
+        || (cont.attrs.is_renamed && !schema_base_name.contains('{'))
+    {
+        quote! {
+            #schema_base_name.to_owned()
+        }
+    } else if cont.attrs.is_renamed {
+        let mut schema_name_fmt = schema_base_name;
+        for tp in &params {
+            schema_name_fmt.push_str(&format!("{{{}:.0}}", tp));
+        }
+        quote! {
+            format!(#schema_name_fmt #(,#type_params=#type_params::schema_name())* #(,#const_params=#const_params)*)
+        }
+    } else {
+        let mut schema_name_fmt = schema_base_name;
+        schema_name_fmt.push_str("_for_{}");
+        schema_name_fmt.push_str(&"_and_{}".repeat(params.len() - 1));
+        quote! {
+            format!(#schema_name_fmt #(,#type_params::schema_name())* #(,#const_params)*)
+        }
+    };
 
     let schema_expr = if repr {
         schema_exprs::expr_for_repr(&cont).map_err(|e| vec![e])?


Test patch:
diff --git a/schemars/tests/expected/schema-name-const-generics.json b/schemars/tests/expected/schema-name-const-generics.json
new file mode 100644
index 0000000..7505b28
--- /dev/null
+++ b/schemars/tests/expected/schema-name-const-generics.json
@@ -0,0 +1,14 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "const-generics-z-42",
+  "type": "object",
+  "required": [
+    "foo"
+  ],
+  "properties": {
+    "foo": {
+      "type": "integer",
+      "format": "int32"
+    }
+  }
+}
\ No newline at end of file
diff --git a/schemars/tests/expected/schema-name-mixed-generics.json b/schemars/tests/expected/schema-name-mixed-generics.json
new file mode 100644
index 0000000..32ac797
--- /dev/null
+++ b/schemars/tests/expected/schema-name-mixed-generics.json
@@ -0,0 +1,63 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "MixedGenericStruct_for_MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String_and_42_and_z",
+  "type": "object",
+  "required": [
+    "foo",
+    "generic"
+  ],
+  "properties": {
+    "foo": {
+      "type": "integer",
+      "format": "int32"
+    },
+    "generic": {
+      "$ref": "#/definitions/MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String"
+    }
+  },
+  "definitions": {
+    "MySimpleStruct": {
+      "type": "object",
+      "required": [
+        "foo"
+      ],
+      "properties": {
+        "foo": {
+          "type": "integer",
+          "format": "int32"
+        }
+      }
+    },
+    "MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String": {
+      "type": "object",
+      "required": [
+        "inner",
+        "t",
+        "u",
+        "v",
+        "w"
+      ],
+      "properties": {
+        "inner": {
+          "$ref": "#/definitions/MySimpleStruct"
+        },
+        "t": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "u": {
+          "type": "null"
+        },
+        "v": {
+          "type": "boolean"
+        },
+        "w": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/schemars/tests/schema_name.rs b/schemars/tests/schema_name.rs
index 59fe0cf..ebd8a52 100644
--- a/schemars/tests/schema_name.rs
+++ b/schemars/tests/schema_name.rs
@@ -49,3 +49,29 @@ fn overriden_with_rename_multiple_type_params() -> TestResult {
         "schema-name-custom",
     )
 }
+
+#[allow(dead_code)]
+#[derive(JsonSchema)]
+#[schemars(rename = "const-generics-{BAR}-")]
+struct ConstGenericStruct<const FOO: usize, const BAR: char> {
+    foo: i32,
+}
+
+#[test]
+fn overriden_with_rename_const_generics() -> TestResult {
+    test_default_generated_schema::<ConstGenericStruct<42, 'z'>>("schema-name-const-generics")
+}
+
+#[allow(dead_code)]
+#[derive(JsonSchema)]
+struct MixedGenericStruct<T, const FOO: usize, const BAR: char> {
+    generic: T,
+    foo: i32,
+}
+
+#[test]
+fn default_name_mixed_generics() -> TestResult {
+    test_default_generated_schema::<MixedGenericStruct<MyStruct<i32, (), bool, Vec<String>>, 42, 'z'>>(
+        "schema-name-mixed-generics",
+    )
+}