Repository: oxidecomputer/typify (rust)

Issue:
Objects fields named `default` generate code that won't compile
Please forgive me if any of this is poorly worded/incorrect.
I am a novice rust programmer and much of this is new to me. 

## Issue

Schema objects with a field named `default` generate two conflicting functions named `default` which breaks compilation.

Observed with current main (3ee46b5).
Cargo-typify 0.0.14 generated other errors (duplicated structs) but those were fixed in main.

## Reproduction (minimal)

Here's a simple schema that reproduces the error:

```json
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "meh",
    "description": "meh",
    "type": "object",
    "definitions": {
        "ExceptionBreakpointsFilter": {
            "type": "object",
            "description": "meh2",
            "properties": {
                "default": {
                    "type": "boolean",
                    "description": "A poorly named field."
                }
            }
        }
    }
}
```

<details><summary>`cargo typify repo.json -o -` (generated rust code)</summary>

```rust
#![allow(clippy::redundant_closure_call)]
#![allow(clippy::needless_lifetimes)]
#![allow(clippy::match_single_binding)]
#![allow(clippy::clone_on_copy)]

use serde::{Deserialize, Serialize};

#[doc = "meh2"]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ExceptionBreakpointsFilter {
    #[doc = "A poorly named field."]
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub default: Option<bool>,
}
impl From<&ExceptionBreakpointsFilter> for ExceptionBreakpointsFilter {
    fn from(value: &ExceptionBreakpointsFilter) -> Self {
        value.clone()
    }
}
impl ExceptionBreakpointsFilter {
    pub fn builder() -> builder::ExceptionBreakpointsFilter {
        builder::ExceptionBreakpointsFilter::default()
    }
}
#[doc = "meh"]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Meh(pub serde_json::Map<String, serde_json::Value>);
impl std::ops::Deref for Meh {
    type Target = serde_json::Map<String, serde_json::Value>;
    fn deref(&self) -> &serde_json::Map<String, serde_json::Value> {
        &self.0
    }
}
impl From<Meh> for serde_json::Map<String, serde_json::Value> {
    fn from(value: Meh) -> Self {
        value.0
    }
}
impl From<&Meh> for Meh {
    fn from(value: &Meh) -> Self {
        value.clone()
    }
}
impl From<serde_json::Map<String, serde_json::Value>> for Meh {
    fn from(value: serde_json::Map<String, serde_json::Value>) -> Self {
        Self(value)
    }
}
pub mod builder {
    #[derive(Clone, Debug)]
    pub struct ExceptionBreakpointsFilter {
        default: Result<Option<bool>, String>,
    }
    impl Default for ExceptionBreakpointsFilter {
        fn default() -> Self {
            Self {
                default: Ok(Default::default()),
            }
        }
    }
    impl ExceptionBreakpointsFilter {
        pub fn default<T>(mut self, value: T) -> Self
        where
            T: std::convert::TryInto<Option<bool>>,
            T::Error: std::fmt::Display,
        {
            self.default = value
                .try_into()
                .map_err(|e| format!("error converting supplied value for default: {}", e));
            self
        }
    }
    impl std::convert::TryFrom<ExceptionBreakpointsFilter> for super::ExceptionBreakpointsFilter {
        type Error = String;
        fn try_from(value: ExceptionBreakpointsFilter) -> Result<Self, String> {
            Ok(Self {
                default: value.default?,
            })
        }
    }
    impl From<super::ExceptionBreakpointsFilter> for ExceptionBreakpointsFilter {
        fn from(value: super::ExceptionBreakpointsFilter) -> Self {
            Self {
                default: Ok(value.default),
            }
        }
    }
}
```

</details>

## Error

```rust
    Checking poop v0.1.0 (/private/tmp/poop)
error[E0061]: this function takes 2 arguments but 0 arguments were supplied
  --> src/lib.rs:22:9
   |
22 |         builder::ExceptionBreakpointsFilter::default()
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- two arguments are missing
   |
note: method defined here
  --> src/lib.rs:62:16
   |
62 |         pub fn default<T>(mut self, value: T) -> Self
   |                ^^^^^^^    --------  --------
help: provide the arguments
   |
22 |         builder::ExceptionBreakpointsFilter::default(/* builder::ExceptionBreakpointsFilter */, /* value */)
   |                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For more information about this error, try `rustc --explain E0061`.
```
## Fix

I manually altered the output renaming the `default` field to `default_`
and added an additional `#[serde(rename = "default")]` so the original (`default`) field is used for serialization/deserialization. That seems to have solved the issue.

<details><summary>diff to generated output (my fix)</summary>

```diff
diff --git a/src/lib.rs b/src/lib.rs
index faadcc5..01b1ed3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3742,8 +3742,8 @@ pub struct ExceptionBreakpointsFilter {
     )]
     pub condition_description: Option<String>,
     #[doc = "Initial value of the filter option. If not specified a value false is assumed."]
-    #[serde(default, skip_serializing_if = "Option::is_none")]
-    pub default: Option<bool>,
+    #[serde(default, skip_serializing_if = "Option::is_none", rename = "default")]
+    pub default_: Option<bool>,
     #[doc = "A help text providing additional information about the exception filter. This string is typically shown as a hover and can be translated."]
     #[serde(default, skip_serializing_if = "Option::is_none")]
     pub description: Option<String>,
@@ -19650,7 +19650,7 @@ pub mod builder {
     #[derive(Clone, Debug)]
     pub struct ExceptionBreakpointsFilter {
         condition_description: Result<Option<String>, String>,
-        default: Result<Option<bool>, String>,
+        default_: Result<Option<bool>, String>,
         description: Result<Option<String>, String>,
         filter: Result<String, String>,
         label: Result<String, String>,
@@ -19660,7 +19660,7 @@ pub mod builder {
         fn default() -> Self {
             Self {
                 condition_description: Ok(Default::default()),
-                default: Ok(Default::default()),
+                default_: Ok(Default::default()),
                 description: Ok(Default::default()),
                 filter: Err("no value supplied for filter".to_string()),
                 label: Err("no value supplied for label".to_string()),
@@ -19682,14 +19682,14 @@ pub mod builder {
             });
             self
         }
-        pub fn default<T>(mut self, value: T) -> Self
+        pub fn default_<T>(mut self, value: T) -> Self
         where
             T: std::convert::TryInto<Option<bool>>,
             T::Error: std::fmt::Display,
         {
-            self.default = value
+            self.default_ = value
                 .try_into()
-                .map_err(|e| format!("error converting supplied value for default: {}", e));
+                .map_err(|e| format!("error converting supplied value for default_: {}", e));
             self
         }
         pub fn description<T>(mut self, value: T) -> Self
@@ -19741,7 +19741,7 @@ pub mod builder {
         fn try_from(value: ExceptionBreakpointsFilter) -> Result<Self, String> {
             Ok(Self {
                 condition_description: value.condition_description?,
-                default: value.default?,
+                default_: value.default_?,
                 description: value.description?,
                 filter: value.filter?,
                 label: value.label?,
@@ -19753,7 +19753,7 @@ pub mod builder {
         fn from(value: super::ExceptionBreakpointsFilter) -> Self {
             Self {
                 condition_description: Ok(value.condition_description),
-                default: Ok(value.default),
+                default_: Ok(value.default_),
                 description: Ok(value.description),
                 filter: Ok(value.filter),
                 label: Ok(value.label),
```

</details>


## Summary

Proposal: Consider auto-renaming fields named `default` to `default_` so generated code compiles.

I know magical renames can be a slippery slope but there's also something to be said for a tool that can handle any (valid) schema out of the box even if it has reserved words.

Failing-fast (typify throwing an error and pointing you to a workaround) would be fine too.

## Reproduction (full json schema)

Here's how I ran into this:
```shell
mkdir /tmp/poop
cd /tmp/poop
cargo init
cargo add serde -F derive
cargo add serde_json
cargo install cargo-typify --git https://github.com/oxidecomputer/typify --rev 3ee46b5
wget https://raw.githubusercontent.com/microsoft/vscode-debugadapter-node/main/debugProtocol.json
cargo typify debugProtocol.json -o src/lib.rs
cargo check
```

## Kudos 

Thanks for creating and open sourcing this. Very very cool. Also congrats on shipping. 🥳 

Pull request:
use Default::default rather than buidler::#type_name::Default to avoid named conflicts
Fixes #428 

Patch:
diff --git a/typify-impl/src/type_entry.rs b/typify-impl/src/type_entry.rs
index a610747..86a795c 100644
--- a/typify-impl/src/type_entry.rs
+++ b/typify-impl/src/type_entry.rs
@@ -1109,7 +1109,7 @@ impl TypeEntry {
                 quote! {
                     impl #type_name {
                         pub fn builder() -> builder::#type_name {
-                            builder::#type_name::default()
+                            Default::default()
                         }
                     }
                 },


Test patch:
diff --git a/cargo-typify/tests/outputs/builder.rs b/cargo-typify/tests/outputs/builder.rs
index 08ffd0b..c2c2a29 100644
--- a/cargo-typify/tests/outputs/builder.rs
+++ b/cargo-typify/tests/outputs/builder.rs
@@ -89,7 +89,7 @@ impl From<&Veggie> for Veggie {
 }
 impl Veggie {
     pub fn builder() -> builder::Veggie {
-        builder::Veggie::default()
+        Default::default()
     }
 }
 #[doc = "A representation of a person, company, organization, or place"]
@@ -114,7 +114,7 @@ impl From<&Veggies> for Veggies {
 }
 impl Veggies {
     pub fn builder() -> builder::Veggies {
-        builder::Veggies::default()
+        Default::default()
     }
 }
 pub mod builder {
diff --git a/typify-impl/tests/generator.out b/typify-impl/tests/generator.out
index fc8eee8..6ec24f1 100644
--- a/typify-impl/tests/generator.out
+++ b/typify-impl/tests/generator.out
@@ -20,7 +20,7 @@ mod types {
     }
     impl AllTheTraits {
         pub fn builder() -> builder::AllTheTraits {
-            builder::AllTheTraits::default()
+            Default::default()
         }
     }
     #[doc = "CompoundType"]
@@ -43,7 +43,7 @@ mod types {
     }
     impl CompoundType {
         pub fn builder() -> builder::CompoundType {
-            builder::CompoundType::default()
+            Default::default()
         }
     }
     #[doc = "Pair"]
@@ -68,7 +68,7 @@ mod types {
     }
     impl Pair {
         pub fn builder() -> builder::Pair {
-            builder::Pair::default()
+            Default::default()
         }
     }
     #[doc = "StringEnum"]