Repository: oxidecomputer/typify
Pull request #434 (2023-11-06 22:14:10)

chrono::Date<chrono::offset::Utc> does not support deserialization
Hi typify team!

This is my first PR to the typify repo and it's to fix a small bug in the typify library, that is that the `Date` option in the json schema doesn't compile because the thing it converts it to `chrono::Date<chrono::offset::Utc>` does not support serde::Deserialize. In addition this type is deprecated and it is suggested to use NaiveDate instead.

Technically this is a breaking change to the library but I'm kind of skeptical it ever worked because it is not compilable & it is not tested in the code as far as I can tell with a simple grep.

Let me know if there is sometime more i need to do to get this PR merged.

Thanks,

ahl: Thanks for this. Could you please add a test here: https://github.com/oxidecomputer/typify/tree/main/typify/tests/schemas

These files are all compiled into rust and then ... compiled as Rust.
pcrumley: @ahl I added test coverage. Thanks for pointing me in correct direction 
ahl: Excellent. Would that have failed to compile on some "couldn't derive `Serialize` because K is not `Serialize`" or something?
pcrumley: ack! i thought it was so simple i didnt need to run it locally. Here i fixed the tests locally for me now. As the code stands in main branch if you replaces this with `chrono::Date<chrono::offset::Utc>` you would get the following error.

```

error[E0277]: the trait bound `Date<Utc>: Deserialize<'_>` is not satisfied
  --> tests/schemas/maps.rs:61:28
   |
61 | pub struct MapWithDateKeys(pub std::collections::HashMap<chrono::Date<chrono::offset::Utc>, Value>);
   |                            ^^^ the trait `Deserialize<'_>` is not implemented for `Date<Utc>`
   |
   = help: the following other types implement trait `Deserialize<'de>`:
   ```

Patch:
diff --git a/README.md b/README.md
index ca066a8..3b14ad1 100644
--- a/README.md
+++ b/README.md
@@ -148,6 +148,6 @@ The resulting types won't enforce those value constraints.
 
 A string schema with `format` set to `uuid` will result in the `uuid::Uuid`
 type; similarly, a `format` of `date` translates to
-`chrono::Date<chrono::offset::Utc>`. For users that don't want dependencies on
+`chrono::naive::NaiveDate`. For users that don't want dependencies on
 `uuid` or `chrono` it would be useful for Typify to optionally represent those
-as `String` (or as some other, consumer-specified type).
\ No newline at end of file
+as `String` (or as some other, consumer-specified type).
diff --git a/typify-impl/src/convert.rs b/typify-impl/src/convert.rs
index f3cb022..01083b1 100644
--- a/typify-impl/src/convert.rs
+++ b/typify-impl/src/convert.rs
@@ -719,7 +719,7 @@ impl TypeSpace {
                 self.uses_chrono = true;
                 Ok((
                     TypeEntry::new_native(
-                        "chrono::Date<chrono::offset::Utc>",
+                        "chrono::naive::NaiveDate",
                         &[TypeSpaceImpl::Display, TypeSpaceImpl::FromStr],
                     ),
                     metadata,


Test patch:
diff --git a/typify/tests/schemas/maps.json b/typify/tests/schemas/maps.json
index 0439129..170faec 100644
--- a/typify/tests/schemas/maps.json
+++ b/typify/tests/schemas/maps.json
@@ -18,7 +18,7 @@
       "type": "string",
       "format": "^a*$"
     },
-    "MapWithDateKeys": {
+    "MapWithDateTimeKeys": {
       "$comment": "test that a type isn't needed for propertyNames",
       "type": "object",
       "additionalProperties": {
@@ -27,6 +27,16 @@
       "propertyNames": {
         "format": "date-time"
       }
+    },
+    "MapWithDateKeys": {
+      "$comment": "test that a type isn't needed for propertyNames",
+      "type": "object",
+      "additionalProperties": {
+        "$ref": "#/definitions/Value"
+      },
+      "propertyNames": {
+        "format": "date"
+      }
     }
   },
   "$comment": "usual case of a map whose name must come from its title",
diff --git a/typify/tests/schemas/maps.rs b/typify/tests/schemas/maps.rs
index 162035d..632a4bc 100644
--- a/typify/tests/schemas/maps.rs
+++ b/typify/tests/schemas/maps.rs
@@ -58,29 +58,52 @@ impl ToString for Eh {
     }
 }
 #[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct MapWithDateKeys(
+pub struct MapWithDateKeys(pub std::collections::HashMap<chrono::naive::NaiveDate, Value>);
+impl std::ops::Deref for MapWithDateKeys {
+    type Target = std::collections::HashMap<chrono::naive::NaiveDate, Value>;
+    fn deref(&self) -> &std::collections::HashMap<chrono::naive::NaiveDate, Value> {
+        &self.0
+    }
+}
+impl From<MapWithDateKeys> for std::collections::HashMap<chrono::naive::NaiveDate, Value> {
+    fn from(value: MapWithDateKeys) -> Self {
+        value.0
+    }
+}
+impl From<&MapWithDateKeys> for MapWithDateKeys {
+    fn from(value: &MapWithDateKeys) -> Self {
+        value.clone()
+    }
+}
+impl From<std::collections::HashMap<chrono::naive::NaiveDate, Value>> for MapWithDateKeys {
+    fn from(value: std::collections::HashMap<chrono::naive::NaiveDate, Value>) -> Self {
+        Self(value)
+    }
+}
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct MapWithDateTimeKeys(
     pub std::collections::HashMap<chrono::DateTime<chrono::offset::Utc>, Value>,
 );
-impl std::ops::Deref for MapWithDateKeys {
+impl std::ops::Deref for MapWithDateTimeKeys {
     type Target = std::collections::HashMap<chrono::DateTime<chrono::offset::Utc>, Value>;
     fn deref(&self) -> &std::collections::HashMap<chrono::DateTime<chrono::offset::Utc>, Value> {
         &self.0
     }
 }
-impl From<MapWithDateKeys>
+impl From<MapWithDateTimeKeys>
     for std::collections::HashMap<chrono::DateTime<chrono::offset::Utc>, Value>
 {
-    fn from(value: MapWithDateKeys) -> Self {
+    fn from(value: MapWithDateTimeKeys) -> Self {
         value.0
     }
 }
-impl From<&MapWithDateKeys> for MapWithDateKeys {
-    fn from(value: &MapWithDateKeys) -> Self {
+impl From<&MapWithDateTimeKeys> for MapWithDateTimeKeys {
+    fn from(value: &MapWithDateTimeKeys) -> Self {
         value.clone()
     }
 }
 impl From<std::collections::HashMap<chrono::DateTime<chrono::offset::Utc>, Value>>
-    for MapWithDateKeys
+    for MapWithDateTimeKeys
 {
     fn from(
         value: std::collections::HashMap<chrono::DateTime<chrono::offset::Utc>, Value>,