use std::{
cell::RefCell,
cmp::Ordering,
collections::{BTreeMap, HashMap, HashSet},
fmt,
rc::Rc,
};
use crate::bundle::{Key, Resource, ResourceBundle};
use super::{
header::{BinHeader, BinReprInfo},
BinIndex, CharsetFamily, Endianness, FormatVersion, ResDescriptor, ResourceReprType,
};
const DATA_FORMAT: &[u8; 4] = b"ResB";
const DATA_VERSION: &[u8; 4] = &[1, 4, 0, 0];
const PADDED_HEADER_SIZE: usize = (size_of::<BinHeader>() + 15) & !0xf;
const REPR_INFO_SIZE: usize = size_of::<BinReprInfo>();
const MAGIC_WORD: &[u8; 2] = &[0xda, 0x27];
const SIZE_OF_STRING_CHAR: u8 = 2;
#[cfg(target_endian = "little")]
const SYSTEM_ENDIANNESS: Endianness = Endianness::Little;
#[cfg(target_endian = "big")]
const SYSTEM_ENDIANNESS: Endianness = Endianness::Big;
#[derive(Debug)]
struct StringResourceData<'a> {
containing_string: Option<&'a str>,
offset: u32,
copy_count: usize,
characters_saved: usize,
}
#[derive(Debug)]
enum BinResourceTypeData<'a> {
String {
string: &'a str,
data: Rc<RefCell<StringResourceData<'a>>>,
},
Array {
children: Vec<BinResourceData<'a>>,
},
Table {
map: BTreeMap<Key<'a>, BinResourceData<'a>>,
},
Binary {
binary: &'a [u8],
},
Integer,
IntVector {
int_vector: &'a [u32],
},
_Alias,
}
impl fmt::Display for BinResourceTypeData<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
BinResourceTypeData::String { .. } => "String",
BinResourceTypeData::Array { .. } => "Array",
BinResourceTypeData::Table { .. } => "Table",
BinResourceTypeData::Binary { .. } => "Binary",
BinResourceTypeData::Integer => "Integer",
BinResourceTypeData::IntVector { .. } => "IntVector",
BinResourceTypeData::_Alias => "Alias",
}
)
}
}
#[derive(Debug)]
struct BinResourceData<'a> {
descriptor: Option<ResDescriptor>,
type_data: BinResourceTypeData<'a>,
}
impl<'a> BinResourceData<'a> {
fn new(type_data: BinResourceTypeData<'a>) -> Self {
Self {
descriptor: None,
type_data,
}
}
}
impl<'a> From<&'a Resource<'a>> for BinResourceData<'a> {
fn from(value: &'a Resource<'a>) -> Self {
match value {
Resource::String(string) => Self::new(BinResourceTypeData::String {
string,
data: Rc::new(RefCell::new(StringResourceData {
containing_string: None,
offset: 0,
copy_count: 0,
characters_saved: 0,
})),
}),
Resource::Array(array) => {
let mut children = Vec::new();
for resource in array {
children.push(Self::from(resource));
}
Self::new(BinResourceTypeData::Array { children })
}
Resource::Table(table) => {
let map = table
.iter()
.map(|(key, resource)| (key.clone(), BinResourceData::from(resource)))
.collect::<BTreeMap<_, _>>();
Self::new(BinResourceTypeData::Table { map })
}
Resource::Binary(binary) => Self::new(BinResourceTypeData::Binary { binary }),
Resource::Integer(integer) => Self {
descriptor: Some(ResDescriptor::new(ResourceReprType::Int, (*integer).into())),
type_data: BinResourceTypeData::Integer,
},
Resource::IntVector(int_vector) => {
Self::new(BinResourceTypeData::IntVector { int_vector })
}
}
}
}
#[derive(Debug)]
pub struct Serializer {
format_version: FormatVersion,
largest_table_entry_count: u32,
}
impl Serializer {
fn new(format_version: FormatVersion) -> Self {
Self {
format_version,
largest_table_entry_count: 0,
}
}
pub fn to_bytes(
bundle: &ResourceBundle,
keys_in_discovery_order: &[Key],
) -> Result<Vec<u8>, BinarySerializerError> {
let format_version = FormatVersion::V2_0;
let write_pool_bundle_checksum = false;
let mut serializer = Self::new(format_version);
let mut root = BinResourceData::from(bundle.root());
let index_field_count = match format_version {
FormatVersion::V1_0 => 0,
FormatVersion::V1_1 => 5,
FormatVersion::V1_2 | FormatVersion::V1_3 => 6,
FormatVersion::V2_0 | FormatVersion::V3_0 => {
if write_pool_bundle_checksum {
8
} else {
7
}
}
};
let index_end = (1 + index_field_count) * size_of::<u32>() as u32;
let mut key_position_map = HashMap::new();
let keys = serializer.build_key_block(
&root,
index_end,
keys_in_discovery_order,
&mut key_position_map,
);
let keys_end = index_end + keys.len() as u32;
let data_16_bit = serializer.build_16_bit_data_block(&mut root, &key_position_map)?;
let data_16_bit_end = keys_end + data_16_bit.len() as u32;
let resources =
serializer.build_resource_block(&mut root, data_16_bit_end, &key_position_map)?;
let resources_end = data_16_bit_end + resources.len() as u32;
let index = serializer.build_index(
bundle,
index_field_count,
keys_end >> 2,
resources_end >> 2,
data_16_bit_end >> 2,
);
let repr_info = BinReprInfo {
size: REPR_INFO_SIZE as u16,
reserved_word: 0,
endianness: SYSTEM_ENDIANNESS,
charset_family: CharsetFamily::Ascii,
size_of_char: SIZE_OF_STRING_CHAR,
reserved_byte: 0,
data_format: *DATA_FORMAT,
format_version: serializer.format_version,
data_version: *DATA_VERSION,
};
let header = BinHeader {
size: PADDED_HEADER_SIZE as u16,
magic: *MAGIC_WORD,
repr_info,
};
let root_descriptor = root.descriptor.ok_or_else(|| {
BinarySerializerError::unexpected("root descriptor was never populated")
})?;
let bundle_struct = BinResBundle {
header,
root_descriptor,
index,
keys: &keys,
data_16_bit: &data_16_bit,
resources: &resources,
};
Vec::<u8>::try_from(bundle_struct)
}
fn collect_keys<'a>(resource: &BinResourceTypeData<'a>, collected_keys: &mut HashSet<Key<'a>>) {
match resource {
BinResourceTypeData::Table { map } => {
for (key, resource) in map {
collected_keys.insert(key.clone());
Self::collect_keys(&resource.type_data, collected_keys);
}
}
BinResourceTypeData::Array { children } => {
for child in children {
Self::collect_keys(&child.type_data, collected_keys);
}
}
_ => (),
}
}
fn build_key_block<'a>(
&self,
root: &BinResourceData,
block_start_position: u32,
keys_in_discovery_order: &[Key<'a>],
key_position_map: &mut HashMap<Key<'a>, u32>,
) -> Vec<u8> {
let mut encountered_keys = HashSet::new();
Self::collect_keys(&root.type_data, &mut encountered_keys);
let sorted_keys = keys_in_discovery_order
.iter()
.filter(|&key| encountered_keys.contains(key));
let mut key_block = Vec::new();
for key in sorted_keys {
key_position_map.insert(key.clone(), block_start_position + key_block.len() as u32);
key_block.append(&mut key.as_bytes().to_vec());
key_block.push(0);
}
let padding = (block_start_position as usize + key_block.len()) % 3;
key_block.resize(key_block.len() + padding, 0xaa);
key_block
}
fn collect_strings<'a>(
resource: &mut BinResourceData<'a>,
strings: &mut HashMap<&'a str, Rc<RefCell<StringResourceData<'a>>>>,
) {
let mut existing_string_data = None;
match &mut resource.type_data {
BinResourceTypeData::String { string, data } => {
if strings.contains_key(string) {
#[expect(clippy::unwrap_used)]
let data = strings.get(string).unwrap();
data.borrow_mut().copy_count += 1;
existing_string_data = Some((string, data));
} else {
strings.insert(string, data.clone());
data.borrow_mut().copy_count += 1;
}
}
BinResourceTypeData::Array { children } => {
for child in children {
Self::collect_strings(child, strings);
}
}
BinResourceTypeData::Table { map, .. } => {
for resource in map.values_mut() {
Self::collect_strings(resource, strings);
}
}
_ => (),
};
if let Some((string, data)) = existing_string_data {
resource.type_data = BinResourceTypeData::String {
string,
data: data.clone(),
}
};
}
fn build_string_data(
&mut self,
root: &mut BinResourceData,
data_16_bit: &mut Vec<u16>,
) -> Result<(), BinarySerializerError> {
let mut strings = HashMap::new();
Self::collect_strings(root, &mut strings);
let count = strings.len();
let mut sorted_strings = strings.keys().cloned().collect::<Vec<_>>();
sorted_strings.sort_unstable_by(cmp_string_descending_suffix_aware);
for (i, string) in sorted_strings.iter().enumerate() {
#[expect(clippy::unwrap_used)]
let data = strings.get(string).unwrap();
if data.borrow().containing_string.is_some() {
continue;
}
let copy_count = data.borrow().copy_count;
data.borrow_mut().characters_saved = (copy_count - 1) * get_total_string_size(string)?;
for suffix in sorted_strings.iter().take(count).skip(i + 1) {
if !string.ends_with(suffix) {
break;
}
if get_string_length_marker_size(suffix)? != 0 {
continue;
}
#[expect(clippy::unwrap_used)]
let suffix_data = strings.get(suffix).unwrap();
suffix_data.borrow_mut().offset =
(string.chars().count() - suffix.chars().count()) as u32;
suffix_data.borrow_mut().containing_string = Some(string);
data.borrow_mut().characters_saved +=
suffix_data.borrow().copy_count * get_total_string_size(suffix)?;
}
}
let mut sorted_strings = strings
.iter()
.collect::<Vec<(&&str, &Rc<RefCell<StringResourceData>>)>>();
sorted_strings.sort_unstable_by(cmp_string_ascending_suffix_aware);
for (string, data) in sorted_strings {
if data.borrow().containing_string.is_some() {
#[expect(clippy::unwrap_used)]
let containing_string = data.borrow().containing_string.unwrap();
let containing_data = strings.get(containing_string).ok_or_else(|| {
BinarySerializerError::unexpected("containing string not present in string map")
})?;
let containing_offset = containing_data.borrow().offset
+ get_string_length_marker_size(containing_string)? as u32;
data.borrow_mut().offset += containing_offset;
continue;
}
data.borrow_mut().offset = data_16_bit.len() as u32;
let length = string.chars().count();
let length_words = get_string_length_marker_size(string)?;
match length_words {
0 => (),
1 => data_16_bit.push(0xdc00 & length as u16),
2 => {
data_16_bit.push(0xdfef + (length >> 16) as u16);
data_16_bit.push(length as u16);
}
3 => {
data_16_bit.push(0xdfff);
data_16_bit.push((length >> 16) as u16);
data_16_bit.push(length as u16);
}
_ => return Err(BinarySerializerError::string_too_long(length)),
};
data_16_bit.append(&mut string.encode_utf16().collect());
data_16_bit.push(0);
}
Ok(())
}
fn build_16_bit_resource_data(
&mut self,
resource: &mut BinResourceData,
data_16_bit: &mut Vec<u16>,
key_position_map: &HashMap<Key, u32>,
) -> Option<u16> {
match &mut resource.type_data {
BinResourceTypeData::String { data, .. } => {
let string_offset = data.borrow().offset;
resource.descriptor = Some(ResDescriptor::new(
ResourceReprType::StringV2,
string_offset,
));
match string_offset <= u16::MAX as u32 {
true => Some(string_offset as u16),
false => None,
}
}
BinResourceTypeData::Array { ref mut children } => {
if children.is_empty() {
return None;
}
let mut data_16_bit_offsets = Vec::new();
for child in &mut *children {
let offset =
self.build_16_bit_resource_data(child, data_16_bit, key_position_map);
data_16_bit_offsets.push(offset);
}
let data_16_bit_offsets = data_16_bit_offsets
.into_iter()
.collect::<Option<Vec<_>>>()?;
resource.descriptor = Some(ResDescriptor::new(
ResourceReprType::Array16,
data_16_bit.len() as u32,
));
data_16_bit.push(children.len() as u16);
for offset in data_16_bit_offsets {
data_16_bit.push(offset);
}
None
}
BinResourceTypeData::Table { ref mut map, .. } => {
if map.is_empty() {
return None;
}
let mut data_16_bit_offsets = Vec::new();
for resource in map.values_mut() {
let offset =
self.build_16_bit_resource_data(resource, data_16_bit, key_position_map);
data_16_bit_offsets.push(offset);
}
let data_16_bit_offsets = data_16_bit_offsets
.into_iter()
.collect::<Option<Vec<_>>>()?;
let size = map.len() as u32;
self.largest_table_entry_count =
std::cmp::max(self.largest_table_entry_count, size);
resource.descriptor = Some(ResDescriptor::new(
ResourceReprType::Table16,
data_16_bit.len() as u32,
));
data_16_bit.push(size as u16);
for position in key_position_map.values() {
data_16_bit.push(*position as u16);
}
for descriptor in data_16_bit_offsets {
data_16_bit.push(descriptor);
}
None
}
_ => None,
}
}
fn build_16_bit_data_block(
&mut self,
root: &mut BinResourceData,
key_position_map: &HashMap<Key, u32>,
) -> Result<Vec<u8>, BinarySerializerError> {
let mut data_16_bit = vec![0];
self.build_string_data(root, &mut data_16_bit)?;
self.build_16_bit_resource_data(root, &mut data_16_bit, key_position_map);
if data_16_bit.len() & 1 != 0 {
data_16_bit.push(0xaaaa);
}
let data_16_bit = data_16_bit
.iter()
.flat_map(|value| value.to_ne_bytes())
.collect();
Ok(data_16_bit)
}
fn build_32_bit_resource(
&mut self,
resource: &mut BinResourceData,
block_start_position: u32,
data: &mut Vec<u8>,
key_position_map: &HashMap<Key, u32>,
) -> Result<ResDescriptor, BinarySerializerError> {
if let Some(descriptor) = resource.descriptor {
return Ok(descriptor);
}
match &mut resource.type_data {
BinResourceTypeData::Array { children } => {
if children.is_empty() {
return Ok(ResDescriptor::_new_empty(ResourceReprType::Array));
}
let child_descriptors = children
.iter_mut()
.map(|child| {
self.build_32_bit_resource(
child,
block_start_position,
data,
key_position_map,
)
})
.collect::<Result<Vec<_>, BinarySerializerError>>()?;
let offset = block_start_position + data.len() as u32;
resource.descriptor =
Some(ResDescriptor::new(ResourceReprType::Array, offset >> 2));
let mut vector = (children.len() as u32).to_ne_bytes().to_vec();
data.append(&mut vector);
for descriptor in child_descriptors {
data.append(&mut u32::from(descriptor).to_ne_bytes().to_vec());
}
}
BinResourceTypeData::Table { map, .. } => {
if map.is_empty() {
return Ok(ResDescriptor::_new_empty(ResourceReprType::Table));
}
let size = map.len() as u32;
self.largest_table_entry_count =
std::cmp::max(self.largest_table_entry_count, size);
let child_descriptors = map
.values_mut()
.map(|child| {
self.build_32_bit_resource(
child,
block_start_position,
data,
key_position_map,
)
})
.collect::<Result<Vec<_>, BinarySerializerError>>()?;
let offset = block_start_position + data.len() as u32;
resource.descriptor =
Some(ResDescriptor::new(ResourceReprType::Table, offset >> 2));
data.append(&mut (map.len() as u16).to_ne_bytes().to_vec());
for key in map.keys() {
let position = key_position_map.get(key).ok_or_else(|| {
BinarySerializerError::unexpected("key not present in position map")
})?;
data.append(&mut (*position as u16).to_ne_bytes().to_vec());
}
if map.len() & 1 == 0 {
data.resize(data.len() + 2, 0xaa);
}
for descriptor in child_descriptors {
data.append(&mut u32::from(descriptor).to_ne_bytes().to_vec());
}
}
BinResourceTypeData::Binary { binary } => {
if binary.is_empty() {
return Ok(ResDescriptor::_new_empty(ResourceReprType::Binary));
}
let offset = block_start_position as usize + data.len();
let aligned = (offset + size_of::<u32>()) % 16;
if aligned != 0 {
data.resize(data.len() + (16 - aligned), 0xaa);
}
let offset = block_start_position + data.len() as u32;
resource.descriptor =
Some(ResDescriptor::new(ResourceReprType::Binary, offset >> 2));
data.append(&mut (binary.len() as u32).to_ne_bytes().to_vec());
data.extend_from_slice(binary);
}
BinResourceTypeData::IntVector { int_vector } => {
if int_vector.is_empty() {
return Ok(ResDescriptor::_new_empty(ResourceReprType::IntVector));
}
let offset = block_start_position + data.len() as u32;
resource.descriptor =
Some(ResDescriptor::new(ResourceReprType::IntVector, offset >> 2));
data.append(&mut (int_vector.len() as u32).to_ne_bytes().to_vec());
for int in *int_vector {
data.append(&mut (*int).to_ne_bytes().to_vec());
}
}
BinResourceTypeData::_Alias => todo!(),
_ => {
return Err(BinarySerializerError::unexpected(
"expected resource to have been processed already",
))
}
};
let position = block_start_position as usize + data.len();
let u32_size = size_of::<u32>();
if position % u32_size != 0 {
data.resize(data.len() + (u32_size - position % u32_size), 0xaa);
}
resource.descriptor.ok_or_else(|| {
BinarySerializerError::unexpected("resource descriptor has not been populated")
})
}
fn build_resource_block(
&mut self,
root: &mut BinResourceData,
block_start_position: u32,
key_position_map: &HashMap<Key, u32>,
) -> Result<Vec<u8>, BinarySerializerError> {
let mut body = Vec::new();
self.build_32_bit_resource(root, block_start_position, &mut body, key_position_map)?;
Ok(body)
}
fn build_index(
&self,
bundle: &ResourceBundle,
field_count: u32,
keys_end: u32,
resources_end: u32,
data_16_bit_end: u32,
) -> BinIndex {
BinIndex {
field_count,
keys_end,
resources_end,
bundle_end: resources_end,
largest_table_entry_count: self.largest_table_entry_count,
bundle_attributes: Some(!bundle.is_locale_fallback_enabled as u32),
data_16_bit_end: Some(data_16_bit_end),
pool_checksum: None,
}
}
}
fn get_total_string_size(string: &str) -> Result<usize, BinarySerializerError> {
Ok(string.chars().count() + get_string_length_marker_size(string)? + 1)
}
fn get_string_length_marker_size(string: &str) -> Result<usize, BinarySerializerError> {
let length = match string.chars().count() {
0..=40 => 0,
41..=0x3ee => 1,
0x3ef..=0xf_ffff => 2,
0x10_0000..=0xffff_ffff => 3,
length => return Err(BinarySerializerError::string_too_long(length)),
};
Ok(length)
}
fn _cmp_key_suffix(l: &(usize, &str), r: &(usize, &str)) -> Ordering {
let (l_pos, l_string) = l;
let (r_pos, r_string) = r;
let mut l_iter = l_string.chars();
let mut r_iter = r_string.chars();
while let Some((l_char, r_char)) = l_iter.next_back().zip(r_iter.next_back()) {
match l_char.cmp(&r_char) {
Ordering::Equal => (),
ord => return ord,
};
}
match r_string.chars().count().cmp(&l_string.chars().count()) {
Ordering::Equal => l_pos.cmp(r_pos),
ord => ord,
}
}
fn cmp_string_descending_suffix_aware(l: &&str, r: &&str) -> Ordering {
let mut l_iter = l.chars();
let mut r_iter = r.chars();
while let Some((l_char, r_char)) = l_iter.next_back().zip(r_iter.next_back()) {
match l_char.cmp(&r_char) {
Ordering::Equal => (),
ord => return ord,
}
}
r.chars().count().cmp(&l.chars().count())
}
fn cmp_string_ascending_suffix_aware(
l: &(&&str, &Rc<RefCell<StringResourceData>>),
r: &(&&str, &Rc<RefCell<StringResourceData>>),
) -> Ordering {
let (l, l_data) = l;
let (r, r_data) = r;
match (
l_data.borrow().containing_string,
r_data.borrow().containing_string,
) {
(Some(_), None) => return Ordering::Greater,
(None, Some(_)) => return Ordering::Less,
_ => (),
};
match l.chars().count().cmp(&r.chars().count()) {
Ordering::Equal => (),
ord => return ord,
};
match r_data
.borrow()
.characters_saved
.cmp(&l_data.borrow().characters_saved)
{
Ordering::Equal => (),
ord => return ord,
};
l.cmp(r)
}
struct BinResBundle<'a> {
header: BinHeader,
root_descriptor: ResDescriptor,
index: BinIndex,
keys: &'a [u8],
data_16_bit: &'a [u8],
resources: &'a [u8],
}
impl TryFrom<BinResBundle<'_>> for Vec<u8> {
type Error = BinarySerializerError;
fn try_from(value: BinResBundle<'_>) -> Result<Self, Self::Error> {
let mut bytes = Vec::new();
bytes.append(&mut Vec::<u8>::from(value.header));
bytes.extend_from_slice(&u32::from(value.root_descriptor).to_ne_bytes());
bytes.append(&mut Vec::<u8>::try_from(value.index)?);
bytes.extend_from_slice(value.keys);
bytes.extend_from_slice(value.data_16_bit);
bytes.extend_from_slice(value.resources);
Ok(bytes)
}
}
impl From<BinHeader> for Vec<u8> {
fn from(value: BinHeader) -> Self {
let mut bytes = Vec::with_capacity(value.size as usize);
bytes.extend_from_slice(&value.size.to_ne_bytes());
bytes.extend_from_slice(&value.magic);
let mut data_info_bytes = Vec::<u8>::from(value.repr_info);
bytes.append(&mut data_info_bytes);
bytes.resize(PADDED_HEADER_SIZE, 0);
bytes
}
}
impl From<BinReprInfo> for Vec<u8> {
fn from(value: BinReprInfo) -> Self {
let mut bytes = Vec::with_capacity(value.size as usize);
bytes.extend_from_slice(&value.size.to_ne_bytes());
bytes.extend_from_slice(&value.reserved_word.to_ne_bytes());
bytes.push(value.endianness.into());
bytes.push(value.charset_family.into());
bytes.push(value.size_of_char);
bytes.push(value.reserved_byte);
bytes.extend_from_slice(&value.data_format);
bytes.extend_from_slice(&<[u8; 4]>::from(value.format_version));
bytes.extend_from_slice(&value.data_version);
bytes
}
}
impl TryFrom<BinIndex> for Vec<u8> {
type Error = BinarySerializerError;
fn try_from(value: BinIndex) -> Result<Self, Self::Error> {
let mut bytes = Vec::with_capacity(value.field_count as usize * size_of::<u32>());
if value.field_count >= 5 {
bytes.extend_from_slice(&value.field_count.to_ne_bytes());
bytes.extend_from_slice(&value.keys_end.to_ne_bytes());
bytes.extend_from_slice(&value.resources_end.to_ne_bytes());
bytes.extend_from_slice(&value.bundle_end.to_ne_bytes());
bytes.extend_from_slice(&value.largest_table_entry_count.to_ne_bytes());
}
if value.field_count >= 6 {
let bundle_attributes = value.bundle_attributes.ok_or_else(|| {
BinarySerializerError::unexpected("no bundle attributes field provided")
})?;
bytes.extend_from_slice(&bundle_attributes.to_ne_bytes());
}
if value.field_count >= 7 {
let data_16_bit_end = value.data_16_bit_end.ok_or_else(|| {
BinarySerializerError::unexpected("no 16-bit data end offset provided")
})?;
bytes.extend_from_slice(&data_16_bit_end.to_ne_bytes());
}
if value.field_count >= 8 {
let pool_checksum = value
.pool_checksum
.ok_or_else(|| BinarySerializerError::unexpected("no pool checksum provided"))?;
bytes.extend_from_slice(&pool_checksum.to_ne_bytes());
}
Ok(bytes)
}
}
impl From<FormatVersion> for [u8; 4] {
fn from(value: FormatVersion) -> [u8; 4] {
match value {
FormatVersion::V1_0 => [1, 0, 0, 0],
FormatVersion::V1_1 => [1, 1, 0, 0],
FormatVersion::V1_2 => [1, 2, 0, 0],
FormatVersion::V1_3 => [1, 3, 0, 0],
FormatVersion::V2_0 => [2, 0, 0, 0],
FormatVersion::V3_0 => [3, 0, 0, 0],
}
}
}
impl From<ResDescriptor> for u32 {
fn from(value: ResDescriptor) -> Self {
((value.resource_type as u32) << 28) | value.value
}
}
#[derive(Debug)]
pub struct BinarySerializerError {
kind: ErrorKind,
}
impl BinarySerializerError {
pub fn string_too_long(length: usize) -> Self {
Self {
kind: ErrorKind::StringTooLong(length),
}
}
pub fn unexpected(message: &'static str) -> Self {
Self {
kind: ErrorKind::Unexpected(message),
}
}
}
impl fmt::Display for BinarySerializerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
ErrorKind::StringTooLong(length) => write!(
f,
"Cannot serialize string of length {length}, maximum is 4,294,967,295 characters"
),
ErrorKind::Unexpected(message) => write!(f, "Unexpected error: {message}"),
}
}
}
#[derive(Debug)]
enum ErrorKind {
StringTooLong(usize),
Unexpected(&'static str),
}