use alloc::vec::Vec;
use zerodds_cdr::{BufferReader, BufferWriter};
use crate::error::{GiopError, GiopResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum ServiceContextTag {
TransactionService = 0,
CodeSets = 1,
ChainBypassCheck = 2,
ChainBypassInfo = 3,
LogicalThreadId = 4,
BiDirIiop = 5,
SendingContextRunTime = 6,
InvocationPolicies = 7,
ForwardedIdentity = 8,
UnknownExceptionInfo = 9,
RtCorbaPriority = 10,
RtCorbaPriorityRange = 11,
FtGroupVersion = 12,
FtRequest = 13,
ExceptionDetailMessage = 14,
SecurityAttributeService = 15,
ActivityService = 16,
}
impl ServiceContextTag {
#[must_use]
pub const fn as_u32(self) -> u32 {
self as u32
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServiceContext {
pub context_id: u32,
pub context_data: Vec<u8>,
}
impl ServiceContext {
#[must_use]
pub fn new(context_id: u32, context_data: Vec<u8>) -> Self {
Self {
context_id,
context_data,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ServiceContextList(pub Vec<ServiceContext>);
impl ServiceContextList {
#[must_use]
pub const fn new() -> Self {
Self(Vec::new())
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn encode(&self, w: &mut BufferWriter) -> GiopResult<()> {
let count = u32::try_from(self.0.len())
.map_err(|_| GiopError::Malformed("service_context list too long".into()))?;
w.write_u32(count)?;
for ctx in &self.0 {
w.write_u32(ctx.context_id)?;
let n = u32::try_from(ctx.context_data.len())
.map_err(|_| GiopError::Malformed("context_data too long".into()))?;
w.write_u32(n)?;
w.write_bytes(&ctx.context_data)?;
}
Ok(())
}
pub fn decode(r: &mut BufferReader<'_>) -> GiopResult<Self> {
let count = r.read_u32()? as usize;
let mut out = Vec::with_capacity(count.min(64));
for _ in 0..count {
let context_id = r.read_u32()?;
let n = r.read_u32()? as usize;
let data = r.read_bytes(n)?;
out.push(ServiceContext {
context_id,
context_data: data.to_vec(),
});
}
Ok(Self(out))
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
use zerodds_cdr::Endianness;
#[test]
fn standard_tag_values_match_spec() {
assert_eq!(ServiceContextTag::TransactionService.as_u32(), 0);
assert_eq!(ServiceContextTag::CodeSets.as_u32(), 1);
assert_eq!(ServiceContextTag::BiDirIiop.as_u32(), 5);
assert_eq!(ServiceContextTag::SecurityAttributeService.as_u32(), 15);
}
#[test]
fn empty_list_round_trip() {
let list = ServiceContextList::default();
let mut w = BufferWriter::new(Endianness::Big);
list.encode(&mut w).unwrap();
let bytes = w.into_bytes();
assert_eq!(bytes.len(), 4);
assert_eq!(&bytes, &[0, 0, 0, 0]);
let mut r = BufferReader::new(&bytes, Endianness::Big);
let decoded = ServiceContextList::decode(&mut r).unwrap();
assert!(decoded.is_empty());
}
#[test]
fn round_trip_with_two_entries_be() {
let list = ServiceContextList(alloc::vec![
ServiceContext::new(1, alloc::vec![0xde, 0xad]),
ServiceContext::new(15, alloc::vec![0xbe, 0xef, 0xca, 0xfe]),
]);
let mut w = BufferWriter::new(Endianness::Big);
list.encode(&mut w).unwrap();
let bytes = w.into_bytes();
let mut r = BufferReader::new(&bytes, Endianness::Big);
let decoded = ServiceContextList::decode(&mut r).unwrap();
assert_eq!(decoded, list);
}
#[test]
fn round_trip_le_byte_order() {
let list = ServiceContextList(alloc::vec![ServiceContext::new(
ServiceContextTag::CodeSets.as_u32(),
alloc::vec![1, 2, 3, 4, 5, 6, 7, 8],
)]);
let mut w = BufferWriter::new(Endianness::Little);
list.encode(&mut w).unwrap();
let bytes = w.into_bytes();
let mut r = BufferReader::new(&bytes, Endianness::Little);
let decoded = ServiceContextList::decode(&mut r).unwrap();
assert_eq!(decoded, list);
}
}