use zerodds_idl::ast::{FloatingType, IntegerType, PrimitiveType};
use crate::error::CppGenError;
pub(crate) const CPP_RESERVED: &[&str] = &[
"alignas",
"alignof",
"and",
"and_eq",
"asm",
"auto",
"bitand",
"bitor",
"bool",
"break",
"case",
"catch",
"char",
"char16_t",
"char32_t",
"class",
"compl",
"const",
"constexpr",
"const_cast",
"continue",
"decltype",
"default",
"delete",
"do",
"double",
"dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
"false",
"float",
"for",
"friend",
"goto",
"if",
"inline",
"int",
"long",
"mutable",
"namespace",
"new",
"noexcept",
"not",
"not_eq",
"nullptr",
"operator",
"or",
"or_eq",
"private",
"protected",
"public",
"register",
"reinterpret_cast",
"return",
"short",
"signed",
"sizeof",
"static",
"static_assert",
"static_cast",
"struct",
"switch",
"template",
"this",
"thread_local",
"throw",
"true",
"try",
"typedef",
"typeid",
"typename",
"union",
"unsigned",
"using",
"virtual",
"void",
"volatile",
"wchar_t",
"while",
"xor",
"xor_eq",
];
#[must_use]
pub fn is_reserved(name: &str) -> bool {
CPP_RESERVED.contains(&name)
}
pub fn check_identifier(name: &str) -> Result<(), CppGenError> {
if is_reserved(name) {
return Err(CppGenError::InvalidName {
name: name.to_string(),
reason: "reserved C++ keyword".to_string(),
});
}
Ok(())
}
#[must_use]
pub fn primitive_to_cpp(p: PrimitiveType) -> &'static str {
match p {
PrimitiveType::Boolean => "bool",
PrimitiveType::Octet => "uint8_t",
PrimitiveType::Char => "char",
PrimitiveType::WideChar => "wchar_t",
PrimitiveType::Integer(i) => integer_to_cpp(i),
PrimitiveType::Floating(f) => floating_to_cpp(f),
}
}
#[must_use]
pub fn integer_to_cpp(i: IntegerType) -> &'static str {
match i {
IntegerType::Short | IntegerType::Int16 => "int16_t",
IntegerType::Long | IntegerType::Int32 => "int32_t",
IntegerType::LongLong | IntegerType::Int64 => "int64_t",
IntegerType::UShort | IntegerType::UInt16 => "uint16_t",
IntegerType::ULong | IntegerType::UInt32 => "uint32_t",
IntegerType::ULongLong | IntegerType::UInt64 => "uint64_t",
IntegerType::Int8 => "int8_t",
IntegerType::UInt8 => "uint8_t",
}
}
#[must_use]
pub fn floating_to_cpp(f: FloatingType) -> &'static str {
match f {
FloatingType::Float => "float",
FloatingType::Double => "double",
FloatingType::LongDouble => "long double",
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used, clippy::panic)]
use super::*;
#[test]
fn primitive_boolean() {
assert_eq!(primitive_to_cpp(PrimitiveType::Boolean), "bool");
}
#[test]
fn primitive_octet() {
assert_eq!(primitive_to_cpp(PrimitiveType::Octet), "uint8_t");
}
#[test]
fn primitive_char() {
assert_eq!(primitive_to_cpp(PrimitiveType::Char), "char");
}
#[test]
fn primitive_wchar() {
assert_eq!(primitive_to_cpp(PrimitiveType::WideChar), "wchar_t");
}
#[test]
fn integer_short_signed_unsigned() {
assert_eq!(integer_to_cpp(IntegerType::Short), "int16_t");
assert_eq!(integer_to_cpp(IntegerType::UShort), "uint16_t");
}
#[test]
fn integer_long_signed_unsigned() {
assert_eq!(integer_to_cpp(IntegerType::Long), "int32_t");
assert_eq!(integer_to_cpp(IntegerType::ULong), "uint32_t");
}
#[test]
fn integer_long_long_signed_unsigned() {
assert_eq!(integer_to_cpp(IntegerType::LongLong), "int64_t");
assert_eq!(integer_to_cpp(IntegerType::ULongLong), "uint64_t");
}
#[test]
fn integer_explicit_widths() {
assert_eq!(integer_to_cpp(IntegerType::Int8), "int8_t");
assert_eq!(integer_to_cpp(IntegerType::UInt8), "uint8_t");
assert_eq!(integer_to_cpp(IntegerType::Int16), "int16_t");
assert_eq!(integer_to_cpp(IntegerType::UInt16), "uint16_t");
assert_eq!(integer_to_cpp(IntegerType::Int32), "int32_t");
assert_eq!(integer_to_cpp(IntegerType::UInt32), "uint32_t");
assert_eq!(integer_to_cpp(IntegerType::Int64), "int64_t");
assert_eq!(integer_to_cpp(IntegerType::UInt64), "uint64_t");
}
#[test]
fn floating_float_double() {
assert_eq!(floating_to_cpp(FloatingType::Float), "float");
assert_eq!(floating_to_cpp(FloatingType::Double), "double");
}
#[test]
fn reserved_class_is_rejected() {
assert!(is_reserved("class"));
assert!(check_identifier("class").is_err());
}
#[test]
fn reserved_int_is_rejected() {
assert!(is_reserved("int"));
}
#[test]
fn non_reserved_identifier_passes() {
assert!(!is_reserved("Foo"));
assert!(check_identifier("Foo").is_ok());
}
#[test]
fn check_returns_invalidname_with_reason() {
let err = check_identifier("template").expect_err("must reject 'template'");
match err {
CppGenError::InvalidName { reason, name } => {
assert_eq!(name, "template");
assert!(reason.to_lowercase().contains("reserved"));
}
other => panic!("unexpected err variant: {other:?}"),
}
}
}