diff --git a/pdl-compiler/src/backends/rust/mod.rs b/pdl-compiler/src/backends/rust/mod.rs index daf8b7f..58b8acb 100644 --- a/pdl-compiler/src/backends/rust/mod.rs +++ b/pdl-compiler/src/backends/rust/mod.rs @@ -994,7 +994,10 @@ fn generate_enum_decl(id: &str, tags: &[ast::Tag], width: usize) -> proc_macro2: }) .collect::>(); ranges.sort_unstable(); - ranges.first().unwrap().0 == 0 + // An enum that declares no value or range tag cannot cover the backing + // integer range, and `first`/`last` would be `None` below. + !ranges.is_empty() + && ranges.first().unwrap().0 == 0 && ranges.last().unwrap().1 == max && ranges.windows(2).all(|window| { if let [left, right] = window { left.1 == right.0 - 1 } else { false } @@ -1055,24 +1058,31 @@ fn generate_enum_decl(id: &str, tags: &[ast::Tag], width: usize) -> proc_macro2: } // Generate the default enum value. - // The default value is the first tag of the enum. - // If the first tag identifies a range, then the first value - // of the range is used. - let default_value = match &tags[0] { - ast::Tag::Value(ast::TagValue { id, .. }) => { + // The default value is the first tag of the enum that is not the default + // (`Other`) tag. If the first tag identifies a range, then the first value + // of the range is used. If the enum declares no tag other than the default + // tag, the default tag is used. + let default_value = match tags.iter().find(|tag| !matches!(tag, ast::Tag::Other(_))) { + Some(ast::Tag::Value(ast::TagValue { id, .. })) => { let id = format_tag_ident(id); quote! { #name::#id } } - ast::Tag::Range(ast::TagRange { tags, .. }) if !tags.is_empty() => { + Some(ast::Tag::Range(ast::TagRange { tags, .. })) if !tags.is_empty() => { let id = format_tag_ident(&tags[0].id); quote! { #name::#id } } - ast::Tag::Range(ast::TagRange { id, range, .. }) => { + Some(ast::Tag::Range(ast::TagRange { id, range, .. })) => { let id = format_tag_ident(id); let value = format_value(*range.start()); quote! { #name::#id(Private(#value)) } } - ast::Tag::Other(_) => todo!(), + // `find` skips `Tag::Other`, so the remaining case is an enum whose + // only declared tag is the default tag. + _ => { + let id = format_tag_ident(&default_tag.as_ref().expect("enum declares at least one tag").id); + let value = format_value(0); + quote! { #name::#id(Private(#value)) } + } }; // Generate the cases for parsing the enum value from an integer. @@ -1483,6 +1493,15 @@ mod tests { B = 1, C = 2..255, } + + enum OpenWithDefaultTagFirst : 8 { + UNKNOWN = .., + A = 0, + } + + enum OpenWithOnlyDefaultTag : 8 { + UNKNOWN = .., + } "# ); diff --git a/pdl-compiler/tests/generated/rust/enum_declaration_big_endian.rs b/pdl-compiler/tests/generated/rust/enum_declaration_big_endian.rs index 60572a7..f3dd9b1 100644 --- a/pdl-compiler/tests/generated/rust/enum_declaration_big_endian.rs +++ b/pdl-compiler/tests/generated/rust/enum_declaration_big_endian.rs @@ -554,3 +554,128 @@ impl From for u64 { u8::from(value) as Self } } +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] +pub enum OpenWithDefaultTagFirst { + A, + Unknown(Private), +} +impl Default for OpenWithDefaultTagFirst { + fn default() -> OpenWithDefaultTagFirst { + OpenWithDefaultTagFirst::A + } +} +impl TryFrom for OpenWithDefaultTagFirst { + type Error = u8; + fn try_from(value: u8) -> Result { + match value { + 0x0 => Ok(OpenWithDefaultTagFirst::A), + 0..=0xff => Ok(OpenWithDefaultTagFirst::Unknown(Private(value))), + } + } +} +impl From<&OpenWithDefaultTagFirst> for u8 { + fn from(value: &OpenWithDefaultTagFirst) -> Self { + match value { + OpenWithDefaultTagFirst::A => 0x0, + OpenWithDefaultTagFirst::Unknown(Private(value)) => *value, + } + } +} +impl From for u8 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + (&value).into() + } +} +impl From for i16 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +impl From for i32 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +impl From for i64 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +impl From for u16 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +impl From for u32 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +impl From for u64 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] +pub enum OpenWithOnlyDefaultTag { + Unknown(Private), +} +impl Default for OpenWithOnlyDefaultTag { + fn default() -> OpenWithOnlyDefaultTag { + OpenWithOnlyDefaultTag::Unknown(Private(0x0)) + } +} +impl TryFrom for OpenWithOnlyDefaultTag { + type Error = u8; + fn try_from(value: u8) -> Result { + match value { + 0..=0xff => Ok(OpenWithOnlyDefaultTag::Unknown(Private(value))), + } + } +} +impl From<&OpenWithOnlyDefaultTag> for u8 { + fn from(value: &OpenWithOnlyDefaultTag) -> Self { + match value { + OpenWithOnlyDefaultTag::Unknown(Private(value)) => *value, + } + } +} +impl From for u8 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + (&value).into() + } +} +impl From for i16 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} +impl From for i32 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} +impl From for i64 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} +impl From for u16 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} +impl From for u32 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} +impl From for u64 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} diff --git a/pdl-compiler/tests/generated/rust/enum_declaration_little_endian.rs b/pdl-compiler/tests/generated/rust/enum_declaration_little_endian.rs index 60572a7..f3dd9b1 100644 --- a/pdl-compiler/tests/generated/rust/enum_declaration_little_endian.rs +++ b/pdl-compiler/tests/generated/rust/enum_declaration_little_endian.rs @@ -554,3 +554,128 @@ impl From for u64 { u8::from(value) as Self } } +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] +pub enum OpenWithDefaultTagFirst { + A, + Unknown(Private), +} +impl Default for OpenWithDefaultTagFirst { + fn default() -> OpenWithDefaultTagFirst { + OpenWithDefaultTagFirst::A + } +} +impl TryFrom for OpenWithDefaultTagFirst { + type Error = u8; + fn try_from(value: u8) -> Result { + match value { + 0x0 => Ok(OpenWithDefaultTagFirst::A), + 0..=0xff => Ok(OpenWithDefaultTagFirst::Unknown(Private(value))), + } + } +} +impl From<&OpenWithDefaultTagFirst> for u8 { + fn from(value: &OpenWithDefaultTagFirst) -> Self { + match value { + OpenWithDefaultTagFirst::A => 0x0, + OpenWithDefaultTagFirst::Unknown(Private(value)) => *value, + } + } +} +impl From for u8 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + (&value).into() + } +} +impl From for i16 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +impl From for i32 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +impl From for i64 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +impl From for u16 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +impl From for u32 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +impl From for u64 { + fn from(value: OpenWithDefaultTagFirst) -> Self { + u8::from(value) as Self + } +} +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] +pub enum OpenWithOnlyDefaultTag { + Unknown(Private), +} +impl Default for OpenWithOnlyDefaultTag { + fn default() -> OpenWithOnlyDefaultTag { + OpenWithOnlyDefaultTag::Unknown(Private(0x0)) + } +} +impl TryFrom for OpenWithOnlyDefaultTag { + type Error = u8; + fn try_from(value: u8) -> Result { + match value { + 0..=0xff => Ok(OpenWithOnlyDefaultTag::Unknown(Private(value))), + } + } +} +impl From<&OpenWithOnlyDefaultTag> for u8 { + fn from(value: &OpenWithOnlyDefaultTag) -> Self { + match value { + OpenWithOnlyDefaultTag::Unknown(Private(value)) => *value, + } + } +} +impl From for u8 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + (&value).into() + } +} +impl From for i16 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} +impl From for i32 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} +impl From for i64 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} +impl From for u16 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} +impl From for u32 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +} +impl From for u64 { + fn from(value: OpenWithOnlyDefaultTag) -> Self { + u8::from(value) as Self + } +}