Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ public String toParamName(String name) {

@Override
public String toEnumValue(String value, String datatype) {
// rust-server templates expect value to be in quotes
// rust-server templates expect value to be in quotes for Display/FromStr
return "\"" + super.toEnumValue(value, datatype) + "\"";
}

Expand Down Expand Up @@ -1565,6 +1565,58 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
public ModelsMap postProcessModels(ModelsMap objs) {
ModelsMap result = super.postProcessModelsEnum(objs);

// Detect integer enums and mark them for serde_repr usage
for (ModelMap modelMap : result.getModels()) {
CodegenModel model = modelMap.getModel();

if (Boolean.TRUE.equals(model.isEnum) &&
(model.isInteger || model.isLong || model.isNumber) &&
model.allowableValues != null) {

// Determine the correct Rust type for the enum's repr
String rustType;
if (model.isNumber && !model.isInteger && !model.isLong) {
// Floating point enum - use dataType or default to f64
rustType = "f32".equals(model.dataType) ? "f32" : "f64";
} else {
// Integer enum - apply the same type fitting logic as properties
rustType = applyIntegerTypeFitting(
model.getFormat(),
model.getMinimum(),
model.getMaximum(),
model.getExclusiveMinimum(),
model.getExclusiveMaximum());
// If applyIntegerTypeFitting returns null, default to i32
if (rustType == null) {
rustType = "i32";
}
}

// Mark this as an integer enum and store the Rust type
model.vendorExtensions.put("x-is-integer-enum", true);
model.vendorExtensions.put("x-rust-type", rustType);

// Set global flag to include serde_repr dependency
additionalProperties.put("apiUsesIntegerEnums", true);

// Add numeric discriminant values for enum variants
@SuppressWarnings("unchecked")
List<Map<String, Object>> enumVars =
(List<Map<String, Object>>) model.allowableValues.get("enumVars");

if (enumVars != null) {
for (Map<String, Object> enumVar : enumVars) {
String value = (String) enumVar.get("value");
if (value != null) {
// Strip quotes to get raw numeric value
String numericValue = value.substring(1, value.length() - 1);
enumVar.put("numericDiscriminant", numericValue);
}
}
}
}
}

// Check for model names that conflict with serde_valid macro internals
// Once we find one, set a class-level flag that persists across all model batches
if (!hasConflictingModelNames) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderVal
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<Vec<{{{classname}}}>>> for hyper::header::HeaderValue {
type Error = String;
Expand All @@ -595,7 +595,7 @@ impl std::convert::TryFrom<header::IntoHeaderValue<Vec<{{{classname}}}>>> for hy
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<Vec<{{{classname}}}>> {
type Error = String;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ regex = "1.12"

serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
{{#apiUsesIntegerEnums}}
serde_repr = "0.1"
{{/apiUsesIntegerEnums}}
serde_valid = { version = "2.0", optional = true }

validator = { version = "0.20", features = ["derive"] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,65 @@ use crate::header;
/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]`
/// which helps with FFI.
#[allow(non_camel_case_types)]
{{#vendorExtensions.x-is-integer-enum}}
#[repr({{{vendorExtensions.x-rust-type}}})]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde_repr::Serialize_repr, serde_repr::Deserialize_repr, Hash)]
{{/vendorExtensions.x-is-integer-enum}}
{{^vendorExtensions.x-is-integer-enum}}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, Hash)]
{{/vendorExtensions.x-is-integer-enum}}
{{^hasConflictingModelNames}}{{^exts.x-skip-serde-valid}}#[cfg_attr(feature = "validate", derive(Validate))]{{/exts.x-skip-serde-valid}}{{/hasConflictingModelNames}}
#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))]{{#xmlName}}
#[serde(rename = "{{{.}}}")]{{/xmlName}}
pub enum {{{classname}}} {
{{#allowableValues}}
{{#enumVars}}
{{^vendorExtensions.x-is-integer-enum}}
#[serde(rename = {{{value}}})]
{{/vendorExtensions.x-is-integer-enum}}
{{#numericDiscriminant}}
{{{name}}} = {{{numericDiscriminant}}},
{{/numericDiscriminant}}
{{^numericDiscriminant}}
{{{name}}},
{{/numericDiscriminant}}
{{/enumVars}}
{{/allowableValues}}
}

impl std::fmt::Display for {{{classname}}} {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
{{#vendorExtensions.x-is-integer-enum}}
write!(f, "{}", *self as {{{vendorExtensions.x-rust-type}}})
{{/vendorExtensions.x-is-integer-enum}}
{{^vendorExtensions.x-is-integer-enum}}
match *self {
{{#allowableValues}}
{{#enumVars}}
{{{classname}}}::{{{name}}} => write!(f, {{{value}}}),
{{/enumVars}}
{{/allowableValues}}
}
{{/vendorExtensions.x-is-integer-enum}}
}
}

impl std::str::FromStr for {{{classname}}} {
type Err = String;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
{{#vendorExtensions.x-is-integer-enum}}
match s.parse::<{{{vendorExtensions.x-rust-type}}}>() {
{{#allowableValues}}
{{#enumVars}}
std::result::Result::Ok({{{numericDiscriminant}}}) => std::result::Result::Ok({{{classname}}}::{{{name}}}),
{{/enumVars}}
{{/allowableValues}}
_ => std::result::Result::Err(format!("Value not valid: {s}")),
}
{{/vendorExtensions.x-is-integer-enum}}
{{^vendorExtensions.x-is-integer-enum}}
match s {
{{#allowableValues}}
{{#enumVars}}
Expand All @@ -66,6 +95,7 @@ impl std::str::FromStr for {{{classname}}} {
{{/allowableValues}}
_ => std::result::Result::Err(format!("Value not valid: {s}")),
}
{{/vendorExtensions.x-is-integer-enum}}
}
}
{{/isEnum}}
Expand Down Expand Up @@ -629,7 +659,7 @@ impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderVal
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<Vec<{{{classname}}}>>> for hyper::header::HeaderValue {
type Error = String;

Expand All @@ -645,7 +675,7 @@ impl std::convert::TryFrom<header::IntoHeaderValue<Vec<{{{classname}}}>>> for hy
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<Vec<{{{classname}}}>> {
type Error = String;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderVal
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<Vec<MultipartRelatedRequest>>> for hyper::header::HeaderValue {
type Error = String;

Expand All @@ -153,7 +153,7 @@ impl std::convert::TryFrom<header::IntoHeaderValue<Vec<MultipartRelatedRequest>>
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<Vec<MultipartRelatedRequest>> {
type Error = String;

Expand Down Expand Up @@ -307,7 +307,7 @@ impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderVal
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<Vec<MultipartRequestObjectField>>> for hyper::header::HeaderValue {
type Error = String;

Expand All @@ -323,7 +323,7 @@ impl std::convert::TryFrom<header::IntoHeaderValue<Vec<MultipartRequestObjectFie
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<Vec<MultipartRequestObjectField>> {
type Error = String;

Expand Down Expand Up @@ -471,7 +471,7 @@ impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderVal
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<Vec<MultipleIdenticalMimeTypesPostRequest>>> for hyper::header::HeaderValue {
type Error = String;

Expand All @@ -487,7 +487,7 @@ impl std::convert::TryFrom<header::IntoHeaderValue<Vec<MultipleIdenticalMimeType
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<Vec<MultipleIdenticalMimeTypesPostRequest>> {
type Error = String;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderVal
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<Vec<OpGetRequest>>> for hyper::header::HeaderValue {
type Error = String;

Expand All @@ -136,7 +136,7 @@ impl std::convert::TryFrom<header::IntoHeaderValue<Vec<OpGetRequest>>> for hyper
}
}

#[cfg(feature = "server")]
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<Vec<OpGetRequest>> {
type Error = String;

Expand Down
Loading
Loading