Repository: GREsau/schemars (rust)

Issue:
The deprecated_enum validates incorrect behavior
Here's the rust type checked by the `deprecated_enum` test:

```rust
#[derive(JsonSchema)]
#[deprecated]
enum DeprecatedEnum {
    Unit,
    #[deprecated]
    DeprecatedUnitVariant,
    #[deprecated]
    DeprecatedStructVariant {
        foo: i32,
        #[deprecated]
        deprecated_field: bool,
    },
}
```

And here's the json output:

```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "DeprecatedEnum",
  "deprecated": true,
  "oneOf": [
    {
      "type": "string",
      "enum": [
        "Unit",
        "DeprecatedUnitVariant"
      ]
    },
    {
      "deprecated": true,
      "type": "object",
      "required": [
        "DeprecatedStructVariant"
      ],
      "properties": {
        "DeprecatedStructVariant": {
          "type": "object",
          "required": [
            "deprecated_field",
            "foo"
          ],
          "properties": {
            "deprecated_field": {
              "deprecated": true,
              "type": "boolean"
            },
            "foo": {
              "type": "integer",
              "format": "int32"
            }
          }
        }
      },
      "additionalProperties": false
    }
  ]
}
```

Note that `DeprecatedUnitVariant` is not marked as `deprecated`. Here's what we'd expect and the diff:

```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "DeprecatedEnum",
  "deprecated": true,
  "oneOf": [
    {
      "type": "string",
      "enum": [
        "Unit"
      ]
    },
    {
      "deprecated": true,
      "type": "string",
      "enum": [
        "DeprecatedUnitVariant"
      ]
    },
    {
      "deprecated": true,
      "type": "object",
      "required": [
        "DeprecatedStructVariant"
      ],
      "properties": {
        "DeprecatedStructVariant": {
          "type": "object",
          "required": [
            "deprecated_field",
            "foo"
          ],
          "properties": {
            "deprecated_field": {
              "deprecated": true,
              "type": "boolean"
            },
            "foo": {
              "type": "integer",
              "format": "int32"
            }
          }
        }
      },
      "additionalProperties": false
    }
  ]
```

```diff
@@ -6,7 +6,13 @@
     {
       "type": "string",
       "enum": [
-        "Unit",
+        "Unit"
+      ]
+    },
+    {
+      "deprecated": true,
+      "type": "string",
+      "enum": [
         "DeprecatedUnitVariant"
       ]
     },
```
```

Pull request:
Fix handling of attributes applied to unit variant types
Fixes #151 

Attributes applied to unit variants are not obeyed. In particular this was evident with `doc` and `deprecated` attributes which were simply ignored.

Patch:
diff --git a/schemars_derive/src/attr/mod.rs b/schemars_derive/src/attr/mod.rs
index b1b9f04..3fad9e4 100644
--- a/schemars_derive/src/attr/mod.rs
+++ b/schemars_derive/src/attr/mod.rs
@@ -185,6 +185,21 @@ impl Attrs {
         }
         self
     }
+
+    pub fn is_default(&self) -> bool {
+        match self {
+            Self {
+                with: None,
+                title: None,
+                description: None,
+                deprecated: false,
+                examples,
+                repr: None,
+                crate_name: None,
+            } if examples.is_empty() => true,
+            _ => false,
+        }
+    }
 }
 
 fn is_known_serde_or_validation_keyword(meta: &syn::Meta) -> bool {
diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs
index c5b1672..1f55084 100644
--- a/schemars_derive/src/schema_exprs.rs
+++ b/schemars_derive/src/schema_exprs.rs
@@ -149,8 +149,7 @@ fn expr_for_external_tagged_enum<'a>(
             unique_names.insert(v.name());
             count += 1;
         })
-        .partition(|v| v.is_unit() && v.attrs.with.is_none());
-
+        .partition(|v| v.is_unit() && v.attrs.is_default());
     let unit_names = unit_variants.iter().map(|v| v.name());
     let unit_schema = schema_object(quote! {
         instance_type: Some(schemars::schema::InstanceType::String.into()),
@@ -168,31 +167,38 @@ fn expr_for_external_tagged_enum<'a>(
 
     schemas.extend(complex_variants.into_iter().map(|variant| {
         let name = variant.name();
-        let sub_schema = expr_for_untagged_enum_variant(variant, deny_unknown_fields);
-
-        let mut schema_expr = schema_object(quote! {
-            instance_type: Some(schemars::schema::InstanceType::Object.into()),
-            object: Some(Box::new(schemars::schema::ObjectValidation {
-                properties: {
-                    let mut props = schemars::Map::new();
-                    props.insert(#name.to_owned(), #sub_schema);
-                    props
-                },
-                required: {
-                    let mut required = schemars::Set::new();
-                    required.insert(#name.to_owned());
-                    required
-                },
-                // Externally tagged variants must prohibit additional
-                // properties irrespective of the disposition of
-                // `deny_unknown_fields`. If additional properties were allowed
-                // one could easily construct an object that validated against
-                // multiple variants since here it's the properties rather than
-                // the values of a property that distingish between variants.
-                additional_properties: Some(Box::new(false.into())),
-                ..Default::default()
-            })),
-        });
+
+        let mut schema_expr = if variant.is_unit() && variant.attrs.with.is_none() {
+            schema_object(quote! {
+                instance_type: Some(schemars::schema::InstanceType::String.into()),
+                enum_values: Some(vec![#name.into()]),
+            })
+        } else {
+            let sub_schema = expr_for_untagged_enum_variant(variant, deny_unknown_fields);
+            schema_object(quote! {
+                instance_type: Some(schemars::schema::InstanceType::Object.into()),
+                object: Some(Box::new(schemars::schema::ObjectValidation {
+                    properties: {
+                        let mut props = schemars::Map::new();
+                        props.insert(#name.to_owned(), #sub_schema);
+                        props
+                    },
+                    required: {
+                        let mut required = schemars::Set::new();
+                        required.insert(#name.to_owned());
+                        required
+                    },
+                    // Externally tagged variants must prohibit additional
+                    // properties irrespective of the disposition of
+                    // `deny_unknown_fields`. If additional properties were allowed
+                    // one could easily construct an object that validated against
+                    // multiple variants since here it's the properties rather than
+                    // the values of a property that distingish between variants.
+                    additional_properties: Some(Box::new(false.into())),
+                    ..Default::default()
+                })),
+            })
+        };
 
         variant
             .attrs


Test patch:
diff --git a/schemars/tests/docs.rs b/schemars/tests/docs.rs
index 2f27771..788140d 100644
--- a/schemars/tests/docs.rs
+++ b/schemars/tests/docs.rs
@@ -31,7 +31,8 @@ struct MyUnitStruct;
 #[doc = " the enum's description."]
 enum MyEnum {
     UndocumentedUnit,
-    /// This comment is not included in the generated schema :(
+    UndocumentedUnit2,
+    /// This comment is included in the generated schema :)
     DocumentedUnit,
     /// ## Complex variant
     /// This is a struct-like variant.
diff --git a/schemars/tests/enum.rs b/schemars/tests/enum.rs
index 2bafefd..3f60e84 100644
--- a/schemars/tests/enum.rs
+++ b/schemars/tests/enum.rs
@@ -118,3 +118,21 @@ enum SimpleInternal {
 fn enum_simple_internal_tag() -> TestResult {
     test_default_generated_schema::<SimpleInternal>("enum-simple-internal")
 }
+
+#[allow(dead_code)]
+#[derive(JsonSchema)]
+enum SoundOfMusic {
+    /// # A deer
+    ///
+    /// A female deer
+    Do,
+    /// A drop of golden sun
+    Re,
+    /// A name I call myself
+    Mi,
+}
+
+#[test]
+fn enum_unit_with_doc_comments() -> TestResult {
+    test_default_generated_schema::<SoundOfMusic>("enum-unit-doc")
+}
diff --git a/schemars/tests/expected/deprecated-enum.json b/schemars/tests/expected/deprecated-enum.json
index 074310f..825ae50 100644
--- a/schemars/tests/expected/deprecated-enum.json
+++ b/schemars/tests/expected/deprecated-enum.json
@@ -6,7 +6,13 @@
     {
       "type": "string",
       "enum": [
-        "Unit",
+        "Unit"
+      ]
+    },
+    {
+      "deprecated": true,
+      "type": "string",
+      "enum": [
         "DeprecatedUnitVariant"
       ]
     },
diff --git a/schemars/tests/expected/doc_comments_enum.json b/schemars/tests/expected/doc_comments_enum.json
index 0adc380..53aaa86 100644
--- a/schemars/tests/expected/doc_comments_enum.json
+++ b/schemars/tests/expected/doc_comments_enum.json
@@ -7,6 +7,13 @@
       "type": "string",
       "enum": [
         "UndocumentedUnit",
+        "UndocumentedUnit2"
+      ]
+    },
+    {
+      "description": "This comment is included in the generated schema :)",
+      "type": "string",
+      "enum": [
         "DocumentedUnit"
       ]
     },
diff --git a/schemars/tests/expected/enum-unit-doc.json b/schemars/tests/expected/enum-unit-doc.json
new file mode 100644
index 0000000..11a5c2a
--- /dev/null
+++ b/schemars/tests/expected/enum-unit-doc.json
@@ -0,0 +1,28 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "SoundOfMusic",
+  "oneOf": [
+    {
+      "title": "A deer",
+      "description": "A female deer",
+      "type": "string",
+      "enum": [
+        "Do"
+      ]
+    },
+    {
+      "description": "A drop of golden sun",
+      "type": "string",
+      "enum": [
+        "Re"
+      ]
+    },
+    {
+      "description": "A name I call myself",
+      "type": "string",
+      "enum": [
+        "Mi"
+      ]
+    }
+  ]
+}
\ No newline at end of file