Skip to content
Merged
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
2 changes: 2 additions & 0 deletions typify-impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use type_entry::{

use crate::util::{sanitize, Case};

pub use crate::util::accept_as_ident;

#[cfg(test)]
mod test_util;

Expand Down
26 changes: 25 additions & 1 deletion typify-impl/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,13 +797,35 @@ pub(crate) fn sanitize(input: &str, case: Case) -> String {
};

// Make sure the string is a valid Rust identifier.
if syn::parse_str::<syn::Ident>(&out).is_ok() {
if accept_as_ident(&out) {
out
} else {
format!("{}_", out)
}
}

/// Return true if the string is a valid Rust identifier.
///
/// If this function returns false, typify adds a trailing underscore to it. For
/// example, `fn` becomes `fn_`.
pub fn accept_as_ident(ident: &str) -> bool {
// Adapted from https://docs.rs/syn/2.0.114/src/syn/ident.rs.html#60-74. The
// main change is adding `gen` to the list.
match ident {
"_" |
// Based on https://doc.rust-lang.org/1.65.0/reference/keywords.html
"abstract" | "as" | "async" | "await" | "become" | "box" | "break" |
"const" | "continue" | "crate" | "do" | "dyn" | "else" | "enum" |
"extern" | "false" | "final" | "fn" | "for" | "gen" | "if" | "impl" |
"in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" |
"override" | "priv" | "pub" | "ref" | "return" | "Self" | "self" |
"static" | "struct" | "super" | "trait" | "true" | "try" | "type" |
"typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" |
"while" | "yield" => false,
_ => true,
}
}

pub(crate) fn recase(input: &str, case: Case) -> (String, Option<String>) {
let new = sanitize(input, case);
let rename = if new == input {
Expand Down Expand Up @@ -1038,6 +1060,8 @@ mod tests {
fn test_sanitize() {
assert_eq!(sanitize("type", Case::Snake), "type_");
assert_eq!(sanitize("ref", Case::Snake), "ref_");
assert_eq!(sanitize("gen", Case::Snake), "gen_");
assert_eq!(sanitize("gen", Case::Pascal), "Gen");
assert_eq!(sanitize("+1", Case::Snake), "plus1");
assert_eq!(sanitize("-1", Case::Snake), "minus1");
assert_eq!(sanitize("@timestamp", Case::Pascal), "Timestamp");
Expand Down
1 change: 1 addition & 0 deletions typify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@

#![deny(missing_docs)]

pub use typify_impl::accept_as_ident;
pub use typify_impl::CrateVers;
pub use typify_impl::Error;
pub use typify_impl::Type;
Expand Down
4 changes: 4 additions & 0 deletions typify/tests/schemas/rust-collisions.json
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@
"for": {
"type": "string"
},
"gen": {
"type": "string"
},
"if": {
"type": "string"
},
Expand Down Expand Up @@ -391,6 +394,7 @@
"false",
"fn",
"for",
"gen",
"if",
"impl",
"in",
Expand Down
20 changes: 20 additions & 0 deletions typify/tests/schemas/rust-collisions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,7 @@ impl Pin {
#[doc = " \"final\","]
#[doc = " \"fn\","]
#[doc = " \"for\","]
#[doc = " \"gen\","]
#[doc = " \"if\","]
#[doc = " \"impl\","]
#[doc = " \"in\","]
Expand Down Expand Up @@ -800,6 +801,9 @@ impl Pin {
#[doc = " \"for\": {"]
#[doc = " \"type\": \"string\""]
#[doc = " },"]
#[doc = " \"gen\": {"]
#[doc = " \"type\": \"string\""]
#[doc = " },"]
#[doc = " \"if\": {"]
#[doc = " \"type\": \"string\""]
#[doc = " },"]
Expand Down Expand Up @@ -937,6 +941,8 @@ pub struct RustKeywordMonster {
pub fn_: ::std::string::String,
#[serde(rename = "for")]
pub for_: ::std::string::String,
#[serde(rename = "gen")]
pub gen_: ::std::string::String,
#[serde(rename = "if")]
pub if_: ::std::string::String,
#[serde(rename = "impl")]
Expand Down Expand Up @@ -2147,6 +2153,7 @@ pub mod builder {
final_: ::std::result::Result<::std::string::String, ::std::string::String>,
fn_: ::std::result::Result<::std::string::String, ::std::string::String>,
for_: ::std::result::Result<::std::string::String, ::std::string::String>,
gen_: ::std::result::Result<::std::string::String, ::std::string::String>,
if_: ::std::result::Result<::std::string::String, ::std::string::String>,
impl_: ::std::result::Result<::std::string::String, ::std::string::String>,
in_: ::std::result::Result<::std::string::String, ::std::string::String>,
Expand Down Expand Up @@ -2201,6 +2208,7 @@ pub mod builder {
final_: Err("no value supplied for final_".to_string()),
fn_: Err("no value supplied for fn_".to_string()),
for_: Err("no value supplied for for_".to_string()),
gen_: Err("no value supplied for gen_".to_string()),
if_: Err("no value supplied for if_".to_string()),
impl_: Err("no value supplied for impl_".to_string()),
in_: Err("no value supplied for in_".to_string()),
Expand Down Expand Up @@ -2426,6 +2434,16 @@ pub mod builder {
.map_err(|e| format!("error converting supplied value for for_: {e}"));
self
}
pub fn gen_<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::string::String>,
T::Error: ::std::fmt::Display,
{
self.gen_ = value
.try_into()
.map_err(|e| format!("error converting supplied value for gen_: {e}"));
self
}
pub fn if_<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::string::String>,
Expand Down Expand Up @@ -2762,6 +2780,7 @@ pub mod builder {
final_: value.final_?,
fn_: value.fn_?,
for_: value.for_?,
gen_: value.gen_?,
if_: value.if_?,
impl_: value.impl_?,
in_: value.in_?,
Expand Down Expand Up @@ -2818,6 +2837,7 @@ pub mod builder {
final_: Ok(value.final_),
fn_: Ok(value.fn_),
for_: Ok(value.for_),
gen_: Ok(value.gen_),
if_: Ok(value.if_),
impl_: Ok(value.impl_),
in_: Ok(value.in_),
Expand Down