use zerodds_idl::ast::types::{BitValue, BitmaskDecl, BitsetDecl};
use crate::error::{Result, RustGenError};
use crate::type_map::{const_expr_as_usize, escape_keyword};
pub fn emit_bitset(out: &mut String, b: &BitsetDecl) -> Result<()> {
let total_bits = b
.bitfields
.iter()
.map(|bf| const_expr_as_usize(&bf.spec.width).unwrap_or(0))
.sum::<usize>();
let storage_type = bitset_storage_type(total_bits);
let name = escape_keyword(&b.name.text);
out.push_str("/// Generated by `zerodds-idl-rust` from IDL bitset.\n");
out.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n");
out.push_str(&format!("pub struct {name} {{\n"));
out.push_str(&format!(" storage: {storage_type},\n"));
out.push_str("}\n\n");
out.push_str(&format!("impl {name} {{\n"));
out.push_str(&format!(
" /// Konstruiert einen `{name}` mit allen Bits = 0.\n"
));
out.push_str(" #[must_use]\n");
out.push_str(" pub const fn new() -> Self {\n");
out.push_str(&format!(
" Self {{ storage: 0 as {storage_type} }}\n"
));
out.push_str(" }\n\n");
out.push_str(&format!(" /// Roh-Storage als `{storage_type}`.\n"));
out.push_str(" #[must_use]\n");
out.push_str(&format!(
" pub const fn as_raw(self) -> {storage_type} {{\n"
));
out.push_str(" self.storage\n");
out.push_str(" }\n\n");
out.push_str(&format!(
" /// Konstruiert aus rohem `{storage_type}`.\n"
));
out.push_str(" #[must_use]\n");
out.push_str(&format!(
" pub const fn from_raw(storage: {storage_type}) -> Self {{\n"
));
out.push_str(" Self { storage }\n");
out.push_str(" }\n");
let mut offset: usize = 0;
for bf in &b.bitfields {
let width = const_expr_as_usize(&bf.spec.width).ok_or(RustGenError::InvalidAnnotation {
name: "bitfield-width".to_string(),
reason: "non-integer width",
})?;
if let Some(field_name) = &bf.name {
emit_bitfield_accessors(out, field_name.text.as_str(), storage_type, offset, width);
}
offset += width;
}
out.push_str("}\n\n");
out.push_str(&format!("impl zerodds_cdr::CdrEncode for {name} {{\n"));
out.push_str(" fn encode(&self, w: &mut zerodds_cdr::BufferWriter) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {\n");
out.push_str(&format!(
" <{storage_type} as zerodds_cdr::CdrEncode>::encode(&self.storage, w)\n"
));
out.push_str(" }\n");
out.push_str("}\n\n");
out.push_str(&format!("impl zerodds_cdr::CdrDecode for {name} {{\n"));
out.push_str(" fn decode(r: &mut zerodds_cdr::BufferReader<'_>) -> ::core::result::Result<Self, zerodds_cdr::DecodeError> {\n");
out.push_str(&format!(
" let storage = <{storage_type} as zerodds_cdr::CdrDecode>::decode(r)?;\n"
));
out.push_str(" ::core::result::Result::Ok(Self { storage })\n");
out.push_str(" }\n");
out.push_str("}\n");
Ok(())
}
fn emit_bitfield_accessors(
out: &mut String,
field_name: &str,
storage_type: &str,
offset: usize,
width: usize,
) {
let escaped_name = escape_keyword(field_name);
let mask: u128 = if width >= 128 {
u128::MAX
} else {
(1u128 << width) - 1
};
if width == 1 {
out.push_str(&format!(
"\n /// Getter fuer `{field_name}` (1 bit @ offset {offset}).\n"
));
out.push_str(" #[must_use]\n");
out.push_str(&format!(
" pub const fn {escaped_name}(self) -> bool {{\n"
));
out.push_str(&format!(" ((self.storage >> {offset}) & 1) != 0\n"));
out.push_str(" }\n");
out.push_str(&format!(
"\n /// Setter fuer `{field_name}` (1 bit @ offset {offset}).\n"
));
out.push_str(&format!(
" pub fn set_{escaped_name}(&mut self, value: bool) {{\n"
));
out.push_str(&format!(
" let mask: {storage_type} = (1 as {storage_type}) << {offset};\n"
));
out.push_str(" if value {\n");
out.push_str(" self.storage |= mask;\n");
out.push_str(" } else {\n");
out.push_str(" self.storage &= !mask;\n");
out.push_str(" }\n");
out.push_str(" }\n");
} else {
out.push_str(&format!(
"\n /// Getter fuer `{field_name}` ({width} bits @ offset {offset}).\n"
));
out.push_str(" #[must_use]\n");
out.push_str(&format!(
" pub const fn {escaped_name}(self) -> {storage_type} {{\n"
));
out.push_str(&format!(
" (self.storage >> {offset}) & ({mask} as {storage_type})\n"
));
out.push_str(" }\n");
out.push_str(&format!(
"\n /// Setter fuer `{field_name}` ({width} bits @ offset {offset}).\n"
));
out.push_str(&format!(
" pub fn set_{escaped_name}(&mut self, value: {storage_type}) {{\n"
));
out.push_str(&format!(
" let mask: {storage_type} = ({mask} as {storage_type}) << {offset};\n"
));
out.push_str(" self.storage = (self.storage & !mask) ");
out.push_str(&format!(
"| ((value & ({mask} as {storage_type})) << {offset});\n"
));
out.push_str(" }\n");
}
let _ = width;
}
fn bitset_storage_type(total_bits: usize) -> &'static str {
match total_bits {
0..=8 => "u8",
9..=16 => "u16",
17..=32 => "u32",
_ => "u64",
}
}
pub fn emit_bitmask(out: &mut String, m: &BitmaskDecl) -> Result<()> {
let bit_count = m.values.len();
let storage_type = bitset_storage_type(bit_count);
let name = escape_keyword(&m.name.text);
out.push_str("/// Generated by `zerodds-idl-rust` from IDL bitmask.\n");
out.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n");
out.push_str(&format!("pub struct {name}({storage_type});\n\n"));
out.push_str(&format!("impl {name} {{\n"));
for (idx, value) in m.values.iter().enumerate() {
emit_bitmask_value(out, value, storage_type, idx);
}
out.push_str("\n /// Konstruiert eine leere Bitmaske (alle Bits = 0).\n");
out.push_str(" #[must_use]\n");
out.push_str(" pub const fn empty() -> Self {\n");
out.push_str(&format!(" Self(0 as {storage_type})\n"));
out.push_str(" }\n");
out.push_str(&format!("\n /// Roh-Storage als `{storage_type}`.\n"));
out.push_str(" #[must_use]\n");
out.push_str(&format!(
" pub const fn bits(self) -> {storage_type} {{\n"
));
out.push_str(" self.0\n");
out.push_str(" }\n");
out.push_str("\n /// Prueft ob alle Bits in `other` gesetzt sind.\n");
out.push_str(" #[must_use]\n");
out.push_str(" pub const fn contains(self, other: Self) -> bool {\n");
out.push_str(" (self.0 & other.0) == other.0\n");
out.push_str(" }\n");
out.push_str("}\n\n");
for op in &["BitOr", "BitAnd", "BitXor"] {
out.push_str(&format!(
"impl ::core::ops::{op} for {name} {{\n type Output = Self;\n fn {fn_name}(self, rhs: Self) -> Self {{\n Self(self.0 {op_sym} rhs.0)\n }}\n}}\n\n",
fn_name = op.to_lowercase(),
op_sym = match *op {
"BitOr" => "|",
"BitAnd" => "&",
"BitXor" => "^",
_ => "?",
}
));
}
out.push_str(&format!("impl zerodds_cdr::CdrEncode for {name} {{\n"));
out.push_str(" fn encode(&self, w: &mut zerodds_cdr::BufferWriter) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {\n");
out.push_str(&format!(
" <{storage_type} as zerodds_cdr::CdrEncode>::encode(&self.0, w)\n"
));
out.push_str(" }\n");
out.push_str("}\n\n");
out.push_str(&format!("impl zerodds_cdr::CdrDecode for {name} {{\n"));
out.push_str(" fn decode(r: &mut zerodds_cdr::BufferReader<'_>) -> ::core::result::Result<Self, zerodds_cdr::DecodeError> {\n");
out.push_str(&format!(
" let v = <{storage_type} as zerodds_cdr::CdrDecode>::decode(r)?;\n"
));
out.push_str(" ::core::result::Result::Ok(Self(v))\n");
out.push_str(" }\n");
out.push_str("}\n");
Ok(())
}
fn emit_bitmask_value(out: &mut String, value: &BitValue, storage_type: &str, position: usize) {
let const_name = value.name.text.to_uppercase();
out.push_str(&format!(
" /// Bit-Wert `{name}` (Position {position}).\n",
name = value.name.text
));
out.push_str(&format!(
" pub const {const_name}: Self = Self((1 as {storage_type}) << {position});\n",
));
}