#![allow(unused)]
use std::fmt::{self, Display};
use std::borrow::Cow;
use std::ops::Range;
use std::slice;
use crate::error::{Error, ErrorKind, Result};
use crate::buf::Buf;
use super::cdr::{CdrRead, CdrReader, CdrWrite, CdrWriter};
use super::ior::{Ior, Profile};
use super::codeset::Codesets;
use super::endian::Endian;
const HEADER_LEN: usize = 12;
const HEADER_LEN_AT: usize = HEADER_LEN - 4;
const FLAGS_AT: usize = 6;
const MAGIC: &[u8; 4] = b"GIOP";
const MAJOR: u8 = 1;
const BODY_ALIGN: usize = 8;
const KIND_REQUEST: u8 = 0;
const KIND_REPLY: u8 = 1;
const KIND_CANCEL_REQUEST: u8 = 2;
const KIND_LOCATE_REQUEST: u8 = 3;
const KIND_LOCATE_REPLY: u8 = 4;
const KIND_CLOSE_CONNECTION: u8 = 5;
const KIND_MESSAGE_ERROR: u8 = 6;
const KIND_FRAGMENT: u8 = 7;
const RESPONSE_EXPECTED: u8 = 0x01;
const RESPONSE_WITH_TARGET: u8 = 0x03;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Version {
V10,
V11,
V12,
V13,
}
impl Version {
#[inline]
pub const fn minor(self) -> u8 {
self as u8
}
#[inline]
pub const fn from_minor(minor: u8) -> Option<Self> {
Some(match minor {
0 => Self::V10,
1 => Self::V11,
2 => Self::V12,
3 => Self::V13,
_ => return None,
})
}
#[inline]
const fn has_fragments(self) -> bool {
!matches!(self, Self::V10)
}
#[inline]
const fn has_fragment_id(self) -> bool {
self.has_v12_layout()
}
#[inline]
const fn has_v12_layout(self) -> bool {
matches!(self, Self::V12 | Self::V13)
}
}
impl Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{MAJOR}.{}", self.minor())
}
}
#[derive(Debug, Clone)]
pub enum Message<'a> {
Incomplete(usize),
Request(Request<'a>),
Reply(Reply<'a>),
CancelRequest(CancelRequest),
LocateRequest(LocateRequest<'a>),
LocateReply(LocateReply),
CloseConnection(Version),
MessageError(Version),
InvalidVersion(u8, u8),
InvalidKind(Version, u8),
Fragment(Fragment),
}
impl<'d> CdrRead<'d> for Message<'d> {
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
let stream = reader.remaining_slice();
let Some(header) = stream.first_chunk::<HEADER_LEN>() else {
return Ok(Self::Incomplete(HEADER_LEN));
};
if &header[0..4] != MAGIC {
return Err(Error::new(ErrorKind::InvalidData)
.with_description("Invalid GIOP signature"));
}
let flags = header[6];
let endian = if flags & 0b01 != 0 { Endian::Little } else { Endian::Big };
let len = header[8..12].try_into().unwrap();
let len = match endian {
Endian::Big => u32::from_be_bytes(len),
Endian::Little => u32::from_le_bytes(len),
};
let total_len = HEADER_LEN + len as usize;
let Some(msg) = stream.get(..total_len) else {
return Ok(Self::Incomplete(total_len));
};
let version = match Version::from_minor(header[5]) {
Some(version) if header[4] == MAJOR => version,
_ => {
reader.skip(total_len)?;
return Ok(Self::InvalidVersion(header[4], header[5]));
}
};
let reserved = if version.has_fragments() { 0b1111_1100 } else { 0b1111_1110 };
if flags & reserved != 0 {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Invalid GIOP flags: {flags:#04X} (version {version})")));
}
let fragment_follows = flags & 0b10 != 0;
let mut msg = reader.substream(msg)
.with_endian(endian)
.with_codesets(reader.codesets());
msg.set_position(HEADER_LEN);
let message = match header[7] {
KIND_REQUEST => Self::Request(Request::read(&mut msg, version, fragment_follows)?),
KIND_REPLY => Self::Reply(Reply::read(&mut msg, version, fragment_follows)?),
KIND_CANCEL_REQUEST => Self::CancelRequest(CancelRequest::read(&mut msg, version)?),
KIND_LOCATE_REQUEST => Self::LocateRequest(LocateRequest::read(&mut msg, version)?),
KIND_LOCATE_REPLY => Self::LocateReply(LocateReply::read(&mut msg, version, fragment_follows)?),
KIND_CLOSE_CONNECTION => Self::CloseConnection(version),
KIND_MESSAGE_ERROR => Self::MessageError(version),
KIND_FRAGMENT if version.has_fragments() => Self::Fragment(Fragment::read(&mut msg, version, fragment_follows)?),
kind => Self::InvalidKind(version, kind),
};
reader.skip(total_len - message.body_len())?;
Ok(message)
}
}
impl CdrWrite for Message<'_> {
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
match self {
Self::Incomplete(total_len) => Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Cannot write a GIOP message that was never fully received, {total_len} octets of it being expected"))),
Self::InvalidVersion(major, minor) => Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Cannot write a GIOP message in the version {major}.{minor}, which is not one this implementation speaks"))),
Self::InvalidKind(version, kind) => Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Cannot write a GIOP message of the kind {kind}, which is not one this implementation has in {version}"))),
Self::Request(msg) => msg.write(writer),
Self::Reply(msg) => msg.write(writer),
Self::CancelRequest(msg) => msg.write(writer),
Self::LocateRequest(msg) => msg.write(writer),
Self::LocateReply(msg) => msg.write(writer),
Self::CloseConnection(version) => write_header(writer, *version, KIND_CLOSE_CONNECTION, false),
Self::MessageError(version) => write_header(writer, *version, KIND_MESSAGE_ERROR, false),
Self::Fragment(msg) => msg.write(writer),
}
}
}
impl Message<'_> {
#[inline]
pub const fn body_len(&self) -> usize {
match self {
Self::Request(msg) => msg.body_len(),
Self::Reply(msg) => msg.body_len(),
Self::LocateReply(msg) => msg.body_len(),
Self::Fragment(msg) => msg.body_len(),
_ => 0,
}
}
#[inline]
pub const fn incomplete_len(&self) -> Option<usize> {
match self {
Self::Incomplete(total_len) => Some(*total_len),
_ => None,
}
}
pub const fn version(&self) -> Option<Version> {
Some(match self {
Self::Incomplete { .. } => return None,
Self::InvalidVersion(..) => return None,
Self::Request(msg) => msg.version,
Self::Reply(msg) => msg.version,
Self::CancelRequest(msg) => msg.version,
Self::LocateRequest(msg) => msg.version,
Self::LocateReply(msg) => msg.version,
Self::CloseConnection(version) |
Self::MessageError(version) |
Self::InvalidKind(version, _) => *version,
Self::Fragment(msg) => msg.version,
})
}
pub const fn fragment_follows(&self) -> bool {
match self {
Self::Request(msg) => msg.fragment_follows,
Self::Reply(msg) => msg.fragment_follows,
Self::LocateReply(msg) => msg.fragment_follows,
Self::Fragment(msg) => msg.fragment_follows,
_ => false,
}
}
pub const fn request_id(&self) -> Option<u32> {
match self {
Self::Request(msg) => Some(msg.request_id),
Self::Reply(msg) => Some(msg.request_id),
Self::CancelRequest(msg) => Some(msg.request_id),
Self::LocateRequest(msg) => Some(msg.request_id),
Self::LocateReply(msg) => Some(msg.request_id),
Self::Fragment(msg) => msg.request_id,
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MessageLen;
impl CdrWrite for MessageLen {
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
let start = writer.origin();
let end = writer.position();
if end - start < HEADER_LEN {
return Err(Error::new(ErrorKind::InvalidData)
.with_description("A GIOP message that does not hold its header has no length to write"));
}
let len = u32::try_from(end - start - HEADER_LEN)
.map_err(|_| Error::new(ErrorKind::InvalidData)
.with_description("A GIOP message is too long for its length to be encoded"))?;
writer.set_position(start + HEADER_LEN_AT);
writer.write_ulong(len)?;
writer.set_position(end);
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MessageWhole;
impl CdrWrite for MessageWhole {
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
let end = writer.position();
if end < HEADER_LEN {
return Err(Error::new(ErrorKind::InvalidData)
.with_description("A GIOP message that does not hold its header cannot be put back together"));
}
let len = u32::try_from(end - HEADER_LEN)
.map_err(|_| Error::new(ErrorKind::InvalidData)
.with_description("A GIOP message is too long for its length to be encoded"))?;
let flags = writer.buffer()[FLAGS_AT];
let len = if flags & 0b01 != 0 { len.to_le_bytes() } else { len.to_be_bytes() };
writer.set_position(FLAGS_AT);
writer.write_octet(flags & !0b10)?;
writer.set_position(HEADER_LEN_AT);
writer.write_octet_slice(&len)?;
writer.set_position(end);
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct Request<'a> {
version: Version,
fragment_follows: bool,
request_id: u32,
response_flags: u8,
target: Target<'a>,
operation: Operation<'a>,
service_contexts: ServiceContexts<'a>,
header: CdrReader<'a>,
endian: Endian,
body_len: u32,
}
impl<'a> Request<'a> {
pub fn new(version: Version, request_id: u32, target: Target<'a>, operation: impl Into<Cow<'a, [u8]>>) -> Self {
Self {
version,
fragment_follows: false,
request_id,
response_flags: RESPONSE_WITH_TARGET,
target,
operation: Operation::Given(operation.into()),
service_contexts: ServiceContexts::Borrowed(&[]),
header: no_header(),
endian: Endian::NATIVE,
body_len: 0,
}
}
#[inline]
pub const fn with_response_expected(mut self, response_expected: bool) -> Self {
self.response_flags = if response_expected { RESPONSE_WITH_TARGET } else { 0 };
self
}
#[inline]
pub const fn with_response_flags(mut self, response_flags: u8) -> Self {
self.response_flags = response_flags;
self
}
#[inline]
pub const fn with_service_contexts(mut self, service_contexts: &'a [ServiceContext]) -> Self {
self.service_contexts = ServiceContexts::Borrowed(service_contexts);
self
}
#[inline]
pub const fn with_fragment_follows(mut self, fragment_follows: bool) -> Self {
self.fragment_follows = fragment_follows;
self
}
#[inline]
pub const fn version(&self) -> Version {
self.version
}
#[inline]
pub const fn request_id(&self) -> u32 {
self.request_id
}
#[inline]
pub const fn response_flags(&self) -> u8 {
self.response_flags
}
#[inline]
pub const fn response_expected(&self) -> bool {
self.response_flags & RESPONSE_EXPECTED != 0
}
#[inline]
pub const fn fragment_follows(&self) -> bool {
self.fragment_follows
}
#[inline]
pub const fn target(&self) -> &Target<'a> {
&self.target
}
#[inline]
pub fn object(&self) -> Option<&[u8]> {
self.target.key()
}
#[inline]
pub fn operation(&self) -> &[u8] {
self.operation.octets(&self.header)
}
#[inline]
pub fn operation_range(&self) -> Option<Range<usize>> {
match &self.operation {
Operation::Given(_) => None,
Operation::Read(at) => Some(at.start as usize..at.end as usize),
}
}
#[inline]
pub fn service_contexts(&self) -> ServiceContextIter<'a> {
self.service_contexts.iter(&self.header, self.endian)
}
pub fn find_codesets(&self) -> Result<Option<Codesets>> {
for context in self.service_contexts() {
match context.and_then(|context| context.parse())? {
Some(ServiceContextKind::Codesets(codesets)) => return Ok(Some(codesets)),
None => {}
}
}
Ok(None)
}
#[inline]
pub const fn body_len(&self) -> usize {
self.body_len as usize
}
#[inline]
pub const fn endian(&self) -> Endian {
self.endian
}
fn read(msg: &mut CdrReader<'a>, version: Version, fragment_follows: bool) -> Result<Self> {
let mut service_contexts = ServiceContexts::Borrowed(&[]);
if !version.has_v12_layout() {
service_contexts = ServiceContexts::skip(msg)?;
}
let request_id = msg.read_ulong()?;
let response_flags = if version.has_v12_layout() {
let flags = msg.read_octet()?;
msg.skip(3)?;
flags
} else if msg.read_bool()? {
RESPONSE_WITH_TARGET
} else {
0
};
let target = read_target(msg, version)?;
let name = msg.read_raw_string()?;
let end = offset(msg.position() - 1)?;
let operation = Operation::Read(end - name.len() as u32..end);
if version.has_v12_layout() {
service_contexts = ServiceContexts::skip(msg)?;
} else {
let len = msg.read_sequence_len()?;
msg.skip(len)?;
}
Ok(Self {
version,
fragment_follows,
request_id,
response_flags,
target,
operation,
service_contexts,
header: message_header(msg, version)?,
endian: msg.endian(),
body_len: body_length(msg, version)?,
})
}
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
write_header(writer, self.version, KIND_REQUEST, self.fragment_follows)?;
if !self.version.has_v12_layout() {
self.service_contexts.write(writer, &self.header, self.endian)?;
}
writer.write_ulong(self.request_id)?;
if self.version.has_v12_layout() {
writer.write_octet(self.response_flags)?;
writer.write_octet_slice(&[0, 0, 0])?;
} else {
writer.write_bool(self.response_expected())?;
}
write_target(writer, &self.target, self.version)?;
writer.write_raw_string(self.operation())?;
if self.version.has_v12_layout() {
self.service_contexts.write(writer, &self.header, self.endian)?;
} else {
writer.write_sequence_len(0)?;
}
write_body_padding(writer, self.version)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum ReplyStatus {
NoException = 0,
UserException = 1,
SystemException = 2,
LocationForward = 3,
LocationForwardPerm = 4,
NeedsAddressingMode = 5,
}
impl ReplyStatus {
#[inline]
const fn from_ulong(status: u32) -> Option<Self> {
Some(match status {
0 => Self::NoException,
1 => Self::UserException,
2 => Self::SystemException,
3 => Self::LocationForward,
4 => Self::LocationForwardPerm,
5 => Self::NeedsAddressingMode,
_ => return None,
})
}
#[inline]
const fn is_known_by(self, version: Version) -> bool {
!matches!(self, Self::LocationForwardPerm | Self::NeedsAddressingMode) || version.has_v12_layout()
}
fn check(self, version: Version) -> Result<Self> {
if self.is_known_by(version) {
Ok(self)
} else {
Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Invalid GIOP reply status: {self:?} (version {version})")))
}
}
fn read(reader: &mut CdrReader<'_>, version: Version) -> Result<Self> {
let raw = reader.read_ulong()?;
Self::from_ulong(raw)
.ok_or_else(|| Error::new(ErrorKind::InvalidData)
.with_description(format!("Invalid GIOP reply status: {raw}")))?
.check(version)
}
}
#[derive(Debug, Clone)]
pub struct Reply<'a> {
version: Version,
fragment_follows: bool,
request_id: u32,
status: ReplyStatus,
service_contexts: ServiceContexts<'a>,
header: CdrReader<'a>,
endian: Endian,
body_len: u32,
}
impl<'a> Reply<'a> {
pub fn new(version: Version, request_id: u32, status: ReplyStatus) -> Self {
Self {
version,
fragment_follows: false,
request_id,
status,
service_contexts: ServiceContexts::Borrowed(&[]),
header: no_header(),
endian: Endian::NATIVE,
body_len: 0,
}
}
#[inline]
pub const fn with_service_contexts(mut self, service_contexts: &'a [ServiceContext]) -> Self {
self.service_contexts = ServiceContexts::Borrowed(service_contexts);
self
}
#[inline]
pub const fn with_fragment_follows(mut self, fragment_follows: bool) -> Self {
self.fragment_follows = fragment_follows;
self
}
#[inline]
pub const fn version(&self) -> Version {
self.version
}
#[inline]
pub const fn request_id(&self) -> u32 {
self.request_id
}
#[inline]
pub const fn status(&self) -> ReplyStatus {
self.status
}
#[inline]
pub const fn fragment_follows(&self) -> bool {
self.fragment_follows
}
#[inline]
pub fn service_contexts(&self) -> ServiceContextIter<'a> {
self.service_contexts.iter(&self.header, self.endian)
}
#[inline]
pub const fn body_len(&self) -> usize {
self.body_len as usize
}
#[inline]
pub const fn endian(&self) -> Endian {
self.endian
}
fn read(msg: &mut CdrReader<'a>, version: Version, fragment_follows: bool) -> Result<Self> {
let mut service_contexts = ServiceContexts::Borrowed(&[]);
if !version.has_v12_layout() {
service_contexts = ServiceContexts::skip(msg)?;
}
let request_id = msg.read_ulong()?;
let status = ReplyStatus::read(msg, version)?;
if version.has_v12_layout() {
service_contexts = ServiceContexts::skip(msg)?;
}
Ok(Self {
version,
fragment_follows,
request_id,
status,
service_contexts,
header: message_header(msg, version)?,
endian: msg.endian(),
body_len: body_length(msg, version)?,
})
}
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
self.status.check(self.version)?;
write_header(writer, self.version, KIND_REPLY, self.fragment_follows)?;
if !self.version.has_v12_layout() {
self.service_contexts.write(writer, &self.header, self.endian)?;
}
writer.write_ulong(self.request_id)?;
writer.write_ulong(self.status as u32)?;
if self.version.has_v12_layout() {
self.service_contexts.write(writer, &self.header, self.endian)?;
}
write_body_padding(writer, self.version)
}
}
#[derive(Debug, Clone, Copy)]
pub struct CancelRequest {
version: Version,
request_id: u32,
}
impl CancelRequest {
#[inline]
pub const fn new(version: Version, request_id: u32) -> Self {
Self {
version,
request_id,
}
}
#[inline]
pub const fn version(&self) -> Version {
self.version
}
#[inline]
pub const fn request_id(&self) -> u32 {
self.request_id
}
fn read(msg: &mut CdrReader<'_>, version: Version) -> Result<Self> {
Ok(Self {
version,
request_id: msg.read_ulong()?,
})
}
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
write_header(writer, self.version, KIND_CANCEL_REQUEST, false)?;
writer.write_ulong(self.request_id)
}
}
#[derive(Debug, Clone)]
pub struct LocateRequest<'a> {
version: Version,
request_id: u32,
target: Target<'a>,
}
impl<'a> LocateRequest<'a> {
#[inline]
pub const fn new(version: Version, request_id: u32, target: Target<'a>) -> Self {
Self {
version,
request_id,
target,
}
}
#[inline]
pub const fn version(&self) -> Version {
self.version
}
#[inline]
pub const fn request_id(&self) -> u32 {
self.request_id
}
#[inline]
pub const fn target(&self) -> &Target<'a> {
&self.target
}
#[inline]
pub fn object(&self) -> Option<&[u8]> {
self.target.key()
}
fn read(msg: &mut CdrReader<'a>, version: Version) -> Result<Self> {
Ok(Self {
version,
request_id: msg.read_ulong()?,
target: read_target(msg, version)?,
})
}
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
write_header(writer, self.version, KIND_LOCATE_REQUEST, false)?;
writer.write_ulong(self.request_id)?;
write_target(writer, &self.target, self.version)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum LocateStatus {
UnknownObject = 0,
ObjectHere = 1,
ObjectForward = 2,
ObjectForwardPerm = 3,
LocSystemException = 4,
LocNeedsAddressingMode = 5,
}
impl LocateStatus {
#[inline]
const fn from_ulong(status: u32) -> Option<Self> {
Some(match status {
0 => Self::UnknownObject,
1 => Self::ObjectHere,
2 => Self::ObjectForward,
3 => Self::ObjectForwardPerm,
4 => Self::LocSystemException,
5 => Self::LocNeedsAddressingMode,
_ => return None,
})
}
#[inline]
const fn is_known_by(self, version: Version) -> bool {
(self as u32) <= Self::ObjectForward as u32 || version.has_v12_layout()
}
fn check(self, version: Version) -> Result<Self> {
if self.is_known_by(version) {
Ok(self)
} else {
Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Invalid GIOP locate status: {self:?} (version {version})")))
}
}
fn read(reader: &mut CdrReader<'_>, version: Version) -> Result<Self> {
let raw = reader.read_ulong()?;
Self::from_ulong(raw)
.ok_or_else(|| Error::new(ErrorKind::InvalidData)
.with_description(format!("Invalid GIOP locate status: {raw}")))?
.check(version)
}
}
#[derive(Debug, Clone)]
pub struct LocateReply {
version: Version,
fragment_follows: bool,
request_id: u32,
status: LocateStatus,
endian: Endian,
body_len: u32,
}
impl LocateReply {
#[inline]
pub const fn new(version: Version, request_id: u32, status: LocateStatus) -> Self {
Self {
version,
fragment_follows: false,
request_id,
status,
endian: Endian::NATIVE,
body_len: 0,
}
}
#[inline]
pub const fn with_fragment_follows(mut self, fragment_follows: bool) -> Self {
self.fragment_follows = fragment_follows;
self
}
#[inline]
pub const fn version(&self) -> Version {
self.version
}
#[inline]
pub const fn request_id(&self) -> u32 {
self.request_id
}
#[inline]
pub const fn status(&self) -> LocateStatus {
self.status
}
#[inline]
pub const fn fragment_follows(&self) -> bool {
self.fragment_follows
}
#[inline]
pub const fn body_len(&self) -> usize {
self.body_len as usize
}
#[inline]
pub const fn endian(&self) -> Endian {
self.endian
}
fn read(msg: &mut CdrReader<'_>, version: Version, fragment_follows: bool) -> Result<Self> {
let request_id = msg.read_ulong()?;
let status = LocateStatus::read(msg, version)?;
Ok(Self {
version,
fragment_follows,
request_id,
status,
endian: msg.endian(),
body_len: body_length(msg, version)?,
})
}
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
self.status.check(self.version)?;
write_header(writer, self.version, KIND_LOCATE_REPLY, self.fragment_follows)?;
writer.write_ulong(self.request_id)?;
writer.write_ulong(self.status as u32)?;
write_body_padding(writer, self.version)
}
}
#[derive(Debug, Clone)]
pub struct Fragment {
version: Version,
fragment_follows: bool,
request_id: Option<u32>,
body_len: u32,
}
impl Fragment {
#[inline]
pub const fn new(version: Version, request_id: u32) -> Self {
Self {
version,
fragment_follows: false,
request_id: Some(request_id),
body_len: 0,
}
}
#[inline]
pub const fn with_fragment_follows(mut self, fragment_follows: bool) -> Self {
self.fragment_follows = fragment_follows;
self
}
#[inline]
pub const fn version(&self) -> Version {
self.version
}
#[inline]
pub const fn request_id(&self) -> Option<u32> {
self.request_id
}
#[inline]
pub const fn fragment_follows(&self) -> bool {
self.fragment_follows
}
#[inline]
pub const fn body_len(&self) -> usize {
self.body_len as usize
}
fn read(msg: &mut CdrReader<'_>, version: Version, fragment_follows: bool) -> Result<Self> {
let request_id = if version.has_fragment_id() {
Some(msg.read_ulong()?)
} else {
None
};
Ok(Self {
version,
fragment_follows,
request_id,
body_len: body_length(msg, Version::V10)?,
})
}
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
write_header(writer, self.version, KIND_FRAGMENT, self.fragment_follows)?;
if self.version.has_fragment_id() {
let request_id = self.request_id
.ok_or_else(|| Error::new(ErrorKind::InvalidData)
.with_description(format!("A fragment of GIOP {} carries the id of the request it continues", self.version)))?;
writer.write_ulong(request_id)?;
}
write_body_padding(writer, Version::V10)
}
}
#[derive(Debug, Clone)]
enum Operation<'a> {
Given(Cow<'a, [u8]>),
Read(Range<u32>),
}
impl<'a> Operation<'a> {
fn octets<'s>(&'s self, msg: &'s CdrReader<'_>) -> &'s [u8] {
match self {
Self::Given(name) => name,
Self::Read(at) => &msg.buffer()[at.start as usize..at.end as usize],
}
}
}
#[derive(Debug, Clone, Copy)]
enum ServiceContexts<'a> {
Borrowed(&'a [ServiceContext]),
Lazy(usize),
}
impl<'a> ServiceContexts<'a> {
fn skip(reader: &mut CdrReader<'_>) -> Result<Self> {
let start = reader.position();
for _ in 0..reader.read_sequence_len()? {
let _ = reader.read_ulong()?; let len = reader.read_sequence_len()?; reader.skip(len)?;
}
Ok(Self::Lazy(start))
}
fn iter(&self, msg: &CdrReader<'a>, endian: Endian) -> ServiceContextIter<'a> {
ServiceContextIter(match *self {
Self::Borrowed(contexts) => Contexts::Borrowed(contexts.iter()),
Self::Lazy(start) => Contexts::Lazy {
reader: message_reader(msg, endian, start),
remaining: None,
},
})
}
fn write(&self, writer: &mut CdrWriter, msg: &CdrReader<'a>, endian: Endian) -> Result<()> {
let start = match *self {
Self::Borrowed(contexts) => return writer.write::<[ServiceContext]>(contexts),
Self::Lazy(start) => start,
};
let mut reader = message_reader(msg, endian, start);
let count = reader.read_sequence_len()?;
writer.write_sequence_len(count)?;
for _ in 0..count {
writer.write(&reader.read::<ServiceContext>()?)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ServiceContextIter<'a>(Contexts<'a>);
#[derive(Debug, Clone)]
enum Contexts<'a> {
Borrowed(slice::Iter<'a, ServiceContext>),
Lazy { reader: CdrReader<'a>, remaining: Option<u32> },
}
impl<'a> Iterator for ServiceContextIter<'a> {
type Item = Result<ServiceContext>;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.0 {
Contexts::Borrowed(contexts) => contexts.next()
.map(|context| Ok(context.clone())),
Contexts::Lazy { reader, remaining } => {
let left = match remaining {
Some(left) => *left,
None => match reader.read_sequence_len() {
Ok(count) => *remaining.insert(count as u32),
Err(err) => {
*remaining = Some(0);
return Some(Err(err));
}
}
};
if left == 0 {
return None;
}
let context = reader.read::<ServiceContext>();
*remaining = Some(if context.is_err() { 0 } else { left - 1 });
Some(context)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServiceContext {
id: u32,
data: Buf<u8, 1>,
}
impl ServiceContext {
pub fn new(id: u32, data: impl Into<Buf<u8, 1>>) -> Self {
Self {
id,
data: data.into(),
}
}
#[inline]
pub const fn id(&self) -> u32 {
self.id
}
#[inline]
pub fn data(&self) -> &[u8] {
self.data.as_slice()
}
pub fn reader(&self) -> Result<CdrReader<'_>> {
let mut reader = CdrReader::new(&self.data);
reader.read_endian()?;
Ok(reader)
}
pub fn parse(&self) -> Result<Option<ServiceContextKind>> {
let mut reader = self.reader()?;
Ok(Some(match self.id {
1 => ServiceContextKind::Codesets(reader.read()?),
_ => return Ok(None),
}))
}
}
impl CdrRead<'_> for ServiceContext {
fn read(reader: &mut CdrReader<'_>) -> Result<Self> {
Ok(Self {
id: reader.read_ulong()?,
data: reader.read()?,
})
}
}
impl CdrWrite for ServiceContext {
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_ulong(self.id)?;
writer.write(&self.data)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ServiceContextKind {
Codesets(Codesets),
}
impl ServiceContextKind {
pub fn build(&self) -> Result<ServiceContext> {
let mut writer = CdrWriter::new();
writer.write_endian()?;
let id = match self {
ServiceContextKind::Codesets(codesets) => {
writer.write(codesets)?;
1
}
};
Ok(ServiceContext {
id,
data: writer.into_buf().freeze(),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i16)]
pub enum Addressing {
Key = 0,
Profile = 1,
Reference = 2,
}
impl Addressing {
#[inline]
const fn from_short(disposition: i16) -> Option<Self> {
Some(match disposition {
0 => Self::Key,
1 => Self::Profile,
2 => Self::Reference,
_ => return None,
})
}
}
impl CdrRead<'_> for Addressing {
fn read(reader: &mut CdrReader<'_>) -> Result<Self> {
let raw = reader.read_short()?;
Self::from_short(raw)
.ok_or_else(|| Error::new(ErrorKind::InvalidData)
.with_description(format!("Invalid GIOP addressing disposition: {raw}")))
}
}
impl CdrWrite for Addressing {
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_short(*self as i16)
}
}
#[derive(Debug, Clone)]
pub enum Target<'a> {
Key(Cow<'a, [u8]>),
Profile(Profile),
Reference(Reference<'a>),
}
impl<'a> Target<'a> {
pub fn new_key(key: impl Into<Cow<'a, [u8]>>) -> Self {
Self::Key(key.into())
}
#[inline]
pub const fn addressing(&self) -> Addressing {
match self {
Self::Key(_) => Addressing::Key,
Self::Profile(_) => Addressing::Profile,
Self::Reference(_) => Addressing::Reference,
}
}
#[inline]
pub fn key(&self) -> Option<&[u8]> {
match self {
Self::Key(key) => Some(key),
_ => None,
}
}
}
impl<'d> CdrRead<'d> for Target<'d> {
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
Ok(match reader.read::<Addressing>()? {
Addressing::Key => Self::Key(Cow::Borrowed(reader.read::<&[u8]>()?)),
Addressing::Profile => Self::Profile(reader.read()?),
Addressing::Reference => Self::Reference(reader.read()?),
})
}
}
impl CdrWrite for Target<'_> {
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write(&self.addressing())?;
match self {
Self::Key(key) => writer.write::<[u8]>(key),
Self::Profile(profile) => writer.write(profile),
Self::Reference(reference) => writer.write(reference),
}
}
}
#[derive(Debug, Clone)]
pub struct Reference<'a> {
selected_profile_index: u32,
ior: Ior<'a>,
}
impl<'a> Reference<'a> {
#[inline]
pub const fn new(selected_profile_index: u32, ior: Ior<'a>) -> Self {
Self {
selected_profile_index,
ior,
}
}
#[inline]
pub const fn selected_profile_index(&self) -> u32 {
self.selected_profile_index
}
#[inline]
pub const fn ior(&self) -> &Ior<'a> {
&self.ior
}
#[inline]
pub fn selected_profile(&self) -> Option<&Profile> {
self.ior.profiles.get(self.selected_profile_index as usize)
}
}
impl<'d> CdrRead<'d> for Reference<'d> {
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
Ok(Self {
selected_profile_index: reader.read_ulong()?,
ior: reader.read()?,
})
}
}
impl CdrWrite for Reference<'_> {
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_ulong(self.selected_profile_index)?;
writer.write(&self.ior)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum CompletionStatus {
Yes = 0,
No = 1,
Maybe = 2,
}
impl CompletionStatus {
#[inline]
const fn from_ulong(status: u32) -> Option<Self> {
Some(match status {
0 => Self::Yes,
1 => Self::No,
2 => Self::Maybe,
_ => return None,
})
}
}
impl Display for CompletionStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Yes => "YES",
Self::No => "NO",
Self::Maybe => "MAYBE",
})
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct SystemException<'a> {
exception_id: Cow<'a, str>,
minor_code: u32,
completion_status: CompletionStatus,
}
impl<'a> SystemException<'a> {
pub fn new(exception_id: impl Into<Cow<'a, str>>) -> Self {
Self {
exception_id: exception_id.into(),
minor_code: 0,
completion_status: CompletionStatus::No,
}
}
#[inline]
pub const fn with_minor_code(mut self, minor_code: u32) -> Self {
self.minor_code = minor_code;
self
}
#[inline]
pub const fn with_completion_status(mut self, completion_status: CompletionStatus) -> Self {
self.completion_status = completion_status;
self
}
#[inline]
pub fn exception_id(&self) -> &str {
&self.exception_id
}
#[inline]
pub fn into_exception_id(self) -> Cow<'a, str> {
self.exception_id
}
#[inline]
pub fn name(&self) -> Option<&'static str> {
Some(standard_exception(&self.exception_id)?.name)
}
#[inline]
pub const fn minor_code(&self) -> u32 {
self.minor_code
}
#[inline]
pub const fn vendor_id(&self) -> u32 {
self.minor_code & VMCID_MASK
}
#[inline]
pub const fn vendor_code(&self) -> u32 {
self.minor_code & !VMCID_MASK
}
pub const fn vendor(&self) -> Option<&'static str> {
Some(match self.vendor_id() {
OMG_VMCID => "OMG",
OMNIORB_VMCID => "omniORB",
JACORB_VMCID => "JacORB",
_ => return None,
})
}
pub fn description(&self) -> Option<&'static str> {
match self.vendor_id() {
OMG_VMCID =>
minor_code(standard_exception(&self.exception_id)?.minor_codes, self.vendor_code()),
OMNIORB_VMCID =>
minor_code(OMNIORB_MINOR_CODES, self.vendor_code()),
JACORB_VMCID if self.exception_id == exception::NO_PERMISSION =>
minor_code(JACORB_NO_PERMISSION_MINOR_CODES, self.vendor_code()),
_ => None,
}
}
#[inline]
pub const fn completion_status(&self) -> CompletionStatus {
self.completion_status
}
}
impl<'d> CdrRead<'d> for SystemException<'d> {
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
let exception_id = reader.read_string()?;
let minor_code = reader.read_ulong()?;
let raw = reader.read_ulong()?;
let completion_status = CompletionStatus::from_ulong(raw)
.ok_or_else(|| Error::new(ErrorKind::InvalidData)
.with_description(format!("Invalid GIOP system exception completion status: {raw}")))?;
Ok(Self {
exception_id,
minor_code,
completion_status,
})
}
}
impl CdrWrite for SystemException<'_> {
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_string(&self.exception_id)?;
writer.write_ulong(self.minor_code)?;
writer.write_ulong(self.completion_status as u32)
}
}
impl Display for SystemException<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.name() {
Some(name) => write!(f, "{name} ({})", self.exception_id)?,
None => write!(f, "{}", self.exception_id)?,
}
if self.minor_code != 0 {
write!(f, ", minor {:#010X}", self.minor_code)?;
match (self.vendor(), self.description()) {
(Some(vendor), Some(description)) => write!(f, " ({vendor} {}: {description})", self.vendor_code())?,
(Some(vendor), None) => write!(f, " ({vendor} {})", self.vendor_code())?,
(None, _) => {}
}
}
write!(f, ", completed {}", self.completion_status)
}
}
impl fmt::Debug for SystemException<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SystemException")
.field("exception_id", &self.exception_id)
.field("name", &self.name())
.field("minor_code", &format_args!("{:#010X}", self.minor_code))
.field("vendor", &self.vendor())
.field("vendor_code", &self.vendor_code())
.field("description", &self.description())
.field("completion_status", &self.completion_status)
.finish()
}
}
const OMG_VMCID: u32 = 0x4F4D_0000;
const OMNIORB_VMCID: u32 = 0x4154_0000;
const JACORB_VMCID: u32 = 0x4A43_0000;
const VMCID_MASK: u32 = 0xFFFF_F000;
pub mod exception {
pub const UNKNOWN: &str = "IDL:omg.org/CORBA/UNKNOWN:1.0";
pub const BAD_PARAM: &str = "IDL:omg.org/CORBA/BAD_PARAM:1.0";
pub const NO_MEMORY: &str = "IDL:omg.org/CORBA/NO_MEMORY:1.0";
pub const IMP_LIMIT: &str = "IDL:omg.org/CORBA/IMP_LIMIT:1.0";
pub const COMM_FAILURE: &str = "IDL:omg.org/CORBA/COMM_FAILURE:1.0";
pub const INV_OBJREF: &str = "IDL:omg.org/CORBA/INV_OBJREF:1.0";
pub const NO_PERMISSION: &str = "IDL:omg.org/CORBA/NO_PERMISSION:1.0";
pub const INTERNAL: &str = "IDL:omg.org/CORBA/INTERNAL:1.0";
pub const MARSHAL: &str = "IDL:omg.org/CORBA/MARSHAL:1.0";
pub const INITIALIZE: &str = "IDL:omg.org/CORBA/INITIALIZE:1.0";
pub const NO_IMPLEMENT: &str = "IDL:omg.org/CORBA/NO_IMPLEMENT:1.0";
pub const BAD_TYPECODE: &str = "IDL:omg.org/CORBA/BAD_TYPECODE:1.0";
pub const BAD_OPERATION: &str = "IDL:omg.org/CORBA/BAD_OPERATION:1.0";
pub const NO_RESOURCES: &str = "IDL:omg.org/CORBA/NO_RESOURCES:1.0";
pub const NO_RESPONSE: &str = "IDL:omg.org/CORBA/NO_RESPONSE:1.0";
pub const PERSIST_STORE: &str = "IDL:omg.org/CORBA/PERSIST_STORE:1.0";
pub const BAD_INV_ORDER: &str = "IDL:omg.org/CORBA/BAD_INV_ORDER:1.0";
pub const TRANSIENT: &str = "IDL:omg.org/CORBA/TRANSIENT:1.0";
pub const FREE_MEM: &str = "IDL:omg.org/CORBA/FREE_MEM:1.0";
pub const INV_IDENT: &str = "IDL:omg.org/CORBA/INV_IDENT:1.0";
pub const INV_FLAG: &str = "IDL:omg.org/CORBA/INV_FLAG:1.0";
pub const INTF_REPOS: &str = "IDL:omg.org/CORBA/INTF_REPOS:1.0";
pub const BAD_CONTEXT: &str = "IDL:omg.org/CORBA/BAD_CONTEXT:1.0";
pub const OBJ_ADAPTER: &str = "IDL:omg.org/CORBA/OBJ_ADAPTER:1.0";
pub const DATA_CONVERSION: &str = "IDL:omg.org/CORBA/DATA_CONVERSION:1.0";
pub const OBJECT_NOT_EXIST: &str = "IDL:omg.org/CORBA/OBJECT_NOT_EXIST:1.0";
pub const TRANSACTION_REQUIRED: &str = "IDL:omg.org/CORBA/TRANSACTION_REQUIRED:1.0";
pub const TRANSACTION_ROLLEDBACK: &str = "IDL:omg.org/CORBA/TRANSACTION_ROLLEDBACK:1.0";
pub const INVALID_TRANSACTION: &str = "IDL:omg.org/CORBA/INVALID_TRANSACTION:1.0";
pub const INV_POLICY: &str = "IDL:omg.org/CORBA/INV_POLICY:1.0";
pub const CODESET_INCOMPATIBLE: &str = "IDL:omg.org/CORBA/CODESET_INCOMPATIBLE:1.0";
pub const REBIND: &str = "IDL:omg.org/CORBA/REBIND:1.0";
pub const TIMEOUT: &str = "IDL:omg.org/CORBA/TIMEOUT:1.0";
pub const TRANSACTION_UNAVAILABLE: &str = "IDL:omg.org/CORBA/TRANSACTION_UNAVAILABLE:1.0";
pub const TRANSACTION_MODE: &str = "IDL:omg.org/CORBA/TRANSACTION_MODE:1.0";
pub const BAD_QOS: &str = "IDL:omg.org/CORBA/BAD_QOS:1.0";
pub const INVALID_ACTIVITY: &str = "IDL:omg.org/CORBA/INVALID_ACTIVITY:1.0";
pub const ACTIVITY_COMPLETED: &str = "IDL:omg.org/CORBA/ACTIVITY_COMPLETED:1.0";
pub const ACTIVITY_REQUIRED: &str = "IDL:omg.org/CORBA/ACTIVITY_REQUIRED:1.0";
}
struct StandardException {
id: &'static str,
name: &'static str,
minor_codes: &'static [&'static str],
}
const STANDARD_EXCEPTIONS: &[StandardException] = &[
StandardException { id: exception::UNKNOWN, name: "UNKNOWN", minor_codes: &[
"unlisted user exception received by client",
"non-standard system exception not supported",
"unknown user exception received by a portable interceptor",
] },
StandardException { id: exception::BAD_PARAM, name: "BAD_PARAM", minor_codes: &[
"failure to register, unregister or look up a value factory",
"RID already defined in the interface repository",
"name already used in the context in the interface repository",
"target is not a valid container",
"name clash in inherited context",
"incorrect type for abstract interface",
"string_to_object conversion failed due to bad scheme name",
"string_to_object conversion failed due to bad address",
"string_to_object conversion failed due to bad scheme specific part",
"string_to_object conversion failed for no specific reason",
"attempt to derive an abstract interface from a non-abstract base",
"attempt to let a ValueDef support more than one non-abstract interface",
"attempt to use an incomplete type code as a parameter",
"invalid object id passed to POA::create_reference_by_id",
"bad name argument in a type code operation",
"bad repository id argument in a type code operation",
"invalid member name in a type code operation",
"duplicate label value in create_union_tc",
"incompatible type code of label and discriminator in create_union_tc",
"supplied discriminator type illegitimate in create_union_tc",
"the any passed to ServerRequest::set_exception holds no exception",
"unlisted user exception passed to ServerRequest::set_exception",
"wchar transmission codeset not in the service context",
"service context is not in the OMG-defined range",
"enum value out of range",
"invalid service context id in a portable interceptor",
"attempt to call register_initial_reference with a null object",
"invalid component id in a portable interceptor",
"invalid profile id in a portable interceptor",
"two or more policies of the same type supplied to set_policy_overrides",
"attempt to define a oneway operation with a result, an out parameter or a user exception",
"DII asked to create a request for an implicit operation",
"an OTS/XA integration call returned XAER_INVAL",
"union branch modifier called with a bad case label discriminator",
"illegal IDL context property name",
"illegal IDL property search string",
"illegal IDL context name",
"non-empty IDL context",
"unsupported RMI/IDL custom value type stream format",
"ORB output stream does not support the ValueOutputStream interface",
"ORB input stream does not support the ValueInputStream interface",
"character support limited to ISO 8859-1 for this object reference",
"attempt to add a Pollable to a second PollableSet",
] },
StandardException { id: exception::NO_MEMORY, name: "NO_MEMORY", minor_codes: &[] },
StandardException { id: exception::IMP_LIMIT, name: "IMP_LIMIT", minor_codes: &[
"unable to use any profile in the IOR",
] },
StandardException { id: exception::COMM_FAILURE, name: "COMM_FAILURE", minor_codes: &[] },
StandardException { id: exception::INV_OBJREF, name: "INV_OBJREF", minor_codes: &[
"wchar codeset support not specified",
"codeset component required for a type using wchar or wstring data",
] },
StandardException { id: exception::NO_PERMISSION, name: "NO_PERMISSION", minor_codes: &[] },
StandardException { id: exception::INTERNAL, name: "INTERNAL", minor_codes: &[
"an OTS/XA integration call returned XAER_RMERR",
"an OTS/XA integration call returned XAER_RMFAIL",
] },
StandardException { id: exception::MARSHAL, name: "MARSHAL", minor_codes: &[
"unable to locate a value factory",
"ServerRequest::set_result called before ServerRequest::ctx",
"the NVList passed to ServerRequest::arguments does not describe all parameters",
"attempt to marshal a local object",
"wchar or wstring data erroneously sent by the client over a GIOP 1.0 connection",
"wchar or wstring data erroneously returned by the server over a GIOP 1.0 connection",
"unsupported RMI/IDL custom value type stream format",
] },
StandardException { id: exception::INITIALIZE, name: "INITIALIZE", minor_codes: &[
"priority range too restricted for the ORB",
] },
StandardException { id: exception::NO_IMPLEMENT, name: "NO_IMPLEMENT", minor_codes: &[
"missing local value implementation",
"incompatible value implementation version",
"unable to use any profile in the IOR",
"attempt to use the DII on a local object",
"Biomolecular Sequence Analysis iterator cannot be reset",
"Biomolecular Sequence Analysis metadata is not available as XML",
"Genomic Maps iterator cannot be reset",
"operation not implemented in the local object",
] },
StandardException { id: exception::BAD_TYPECODE, name: "BAD_TYPECODE", minor_codes: &[
"attempt to marshal an incomplete type code",
"member type code illegitimate in a type code operation",
"illegal parameter type",
] },
StandardException { id: exception::BAD_OPERATION, name: "BAD_OPERATION", minor_codes: &[
"the servant manager returned the wrong servant type",
"operation or attribute not known to the target object",
] },
StandardException { id: exception::NO_RESOURCES, name: "NO_RESOURCES", minor_codes: &[
"portable interceptor operation not supported in this binding",
"no connection for the priority of the request",
] },
StandardException { id: exception::NO_RESPONSE, name: "NO_RESPONSE", minor_codes: &[
"the reply is not available immediately in a non-blocking call",
] },
StandardException { id: exception::PERSIST_STORE, name: "PERSIST_STORE", minor_codes: &[] },
StandardException { id: exception::BAD_INV_ORDER, name: "BAD_INV_ORDER", minor_codes: &[
"a dependency in the interface repository prevents destroying this object",
"attempt to destroy indestructible objects in the interface repository",
"the operation would deadlock",
"the ORB has shut down",
"attempt to send or invoke the same Request more than once",
"attempt to set a servant manager after one has already been set",
"ServerRequest::arguments called more than once or after set_exception",
"ServerRequest::ctx called out of order",
"ServerRequest::set_result called out of order",
"attempt to send a DII request that was sent previously",
"attempt to poll a DII request or take its result before it was sent",
"attempt to take the result of a DII request that was retrieved previously",
"attempt to poll a synchronous DII request or to take its result",
"invalid portable interceptor call",
"a service context of that id already exists",
"a PolicyFactory is already registered for that policy type",
"the POA cannot create POAs while it is being destroyed",
"attempt to reassign priority",
"an OTS/XA integration xa_start call returned XAER_OUTSIDE",
"an OTS/XA integration call returned XAER_PROTO",
"transaction context of request and client threads do not match in the interceptor",
"the poller has not returned any response yet",
"a TaggedProfileFactory is already registered for that id",
"a TaggedComponentFactory is already registered for that id",
"the iteration has no more elements",
"invoking this operation is not allowed in post_init",
] },
StandardException { id: exception::TRANSIENT, name: "TRANSIENT", minor_codes: &[
"request discarded because the POA is out of resources or discarding",
"no usable profile in the IOR",
"request cancelled",
"POA destroyed",
] },
StandardException { id: exception::FREE_MEM, name: "FREE_MEM", minor_codes: &[] },
StandardException { id: exception::INV_IDENT, name: "INV_IDENT", minor_codes: &[] },
StandardException { id: exception::INV_FLAG, name: "INV_FLAG", minor_codes: &[] },
StandardException { id: exception::INTF_REPOS, name: "INTF_REPOS", minor_codes: &[
"the interface repository is not available",
"no entry for the requested interface in the interface repository",
] },
StandardException { id: exception::BAD_CONTEXT, name: "BAD_CONTEXT", minor_codes: &[
"IDL context not found",
"no matching IDL context property",
] },
StandardException { id: exception::OBJ_ADAPTER, name: "OBJ_ADAPTER", minor_codes: &[
"system exception in AdapterActivator::unknown_adapter",
"incorrect servant type returned by the servant manager",
"no default servant available",
"no servant manager available",
"violation of the POA policy by ServantActivator::incarnate",
"exception in IORInterceptor::components_established",
"null servant returned by the servant manager",
] },
StandardException { id: exception::DATA_CONVERSION, name: "DATA_CONVERSION", minor_codes: &[
"the character does not map to the negociated transmission codeset",
"failure of the PriorityMapping object",
] },
StandardException { id: exception::OBJECT_NOT_EXIST, name: "OBJECT_NOT_EXIST", minor_codes: &[
"attempt to pass an unactivated value as an object reference",
"failed to create or locate the object adapter",
"the Biomolecular Sequence Analysis service is no longer available",
"object adapter inactive",
"this poller has already delivered a reply to some client",
] },
StandardException { id: exception::TRANSACTION_REQUIRED, name: "TRANSACTION_REQUIRED", minor_codes: &[] },
StandardException { id: exception::TRANSACTION_ROLLEDBACK, name: "TRANSACTION_ROLLEDBACK", minor_codes: &[
"an OTS/XA integration call returned XAER_RB",
"an OTS/XA integration call returned XAER_NOTA",
"OTS/XA integration end was called with success while a rollback was deferred",
"deferred transaction rolled back",
] },
StandardException { id: exception::INVALID_TRANSACTION, name: "INVALID_TRANSACTION", minor_codes: &[] },
StandardException { id: exception::INV_POLICY, name: "INV_POLICY", minor_codes: &[
"unable to reconcile the policy of the IOR with the effective override",
"invalid policy type",
"no PolicyFactory registered for that policy type",
] },
StandardException { id: exception::CODESET_INCOMPATIBLE, name: "CODESET_INCOMPATIBLE", minor_codes: &[
"codeset negociation failed",
"the codeset of the CodeSetContext is not one the server transmits in",
] },
StandardException { id: exception::REBIND, name: "REBIND", minor_codes: &[] },
StandardException { id: exception::TIMEOUT, name: "TIMEOUT", minor_codes: &[
"the reply is not in the poller by the timeout set for it",
"the end time of the request policy has passed",
"the end time of the reply policy has passed",
] },
StandardException { id: exception::TRANSACTION_UNAVAILABLE, name: "TRANSACTION_UNAVAILABLE", minor_codes: &[] },
StandardException { id: exception::TRANSACTION_MODE, name: "TRANSACTION_MODE", minor_codes: &[] },
StandardException { id: exception::BAD_QOS, name: "BAD_QOS", minor_codes: &[] },
StandardException { id: exception::INVALID_ACTIVITY, name: "INVALID_ACTIVITY", minor_codes: &[
"activity or transaction resumed in the wrong context, or invocation incompatible with its state",
] },
StandardException { id: exception::ACTIVITY_COMPLETED, name: "ACTIVITY_COMPLETED", minor_codes: &[
"the activity context completed through a timeout, or otherwise than requested",
] },
StandardException { id: exception::ACTIVITY_REQUIRED, name: "ACTIVITY_REQUIRED", minor_codes: &[
"the calling thread lacks the required activity context",
] },
];
const OMNIORB_MINOR_CODES: &[&str] = &[
"failed on a forwarded location",
"connect failed",
"failed to marshal the arguments",
"failed to unmarshal the arguments",
"failed to marshal the results",
"failed to unmarshal the results",
"connection lost while waiting for the reply",
"call timed out on the client",
"invalid variable length component size",
"read past the end of the message",
"message size exceeds the limit of the client",
"message size exceeds the limit of the server",
"transport error while initialising",
"the bidirectional connection is gone",
"bidirectional connection not allowed",
"bidirectional connection used with no POA",
"connection closed",
"sequence is too long",
"string is too long",
"wide string is too long",
"index out of range",
"string does not end with a nul",
"attempt to invoke a pseudo object remotely",
"the codeset is not known yet",
"codeset not supported",
"invalid enum value",
"invalid union discriminator value",
"attempt to write to a read only buffer",
"invalid initial size",
"invalid ORB initialisation arguments",
"BOA initialisation failed",
"POA initialisation failed",
"the BOA is not initialised",
"the BOA is not initialised",
"invalid servant",
"unsupported",
"the object is a pseudo object",
"unrecognised operation name",
"the interface repository is not available",
"range error",
"bad input",
"invalid fixed point value",
"invalid object reference",
"ORB initialisation failed",
"failed to load a library",
"wide character out of range",
"invalid wide character size",
"invocation through a nil object reference",
"an internal invariant was broken",
"configuration file error",
"initial reference not found",
"invalid IOR",
"the POA is not initialised",
"corrupted object reference",
"interface mismatch",
"the calling thread is not an omnithread",
"unexpected null string",
"the POA is not initialised",
"invalid POA name",
"a local object was expected",
"invalid system id",
"the object was deactivated while the request was held",
"servant already active",
"invalid NVList",
"arguments was never called",
"error in the dynamic implementation",
"an exception in the DSI was not propagated",
"the primary interface returned zero",
"message size exceeds the limit",
"invalid type code",
"the type code is nil",
"the any does not hold a string",
"empty context pattern",
"invalid context name",
"no matching property",
"starting scope not found",
"invalid context",
"invalid DynAny",
"invalid context list",
"invalid exception",
"invalid exception list",
"invalid environment",
"invalid any",
"invalid named value",
"invalid request",
"request configured out of order",
"invalid context list",
"wrong Python type",
"invalid type code operation",
"invalid indirection",
"invalid type code kind",
"incomplete Python type",
"message too long",
"unknown type code kind",
"Python value out of range",
"embedded nul in a Python string",
"incompatible servant",
"Python exception",
"no Python method",
"string is too long",
"sequence is too long",
"the DynAny was destroyed",
"the DynAny is not initialised",
"no Python type for the pseudo object",
"unresolved recursive type code",
"Python exception in the ORB",
"wrong union member selected",
"invalid type code indirection",
"invalid fixed point limits",
"cannot reserve output space",
"invalid chunked encoding",
"invalid value tag",
"no repository id in the value type",
"the type is not a value type",
"attempt to marshal an abstract value",
"type codes not equivalent",
"the shortcut servant was deactivated",
"cannot open the log file",
"failed to publish an endpoint",
"the ORB is not initialised",
"allocation failed",
"unable to start a thread",
"omnithread exception",
"call timed out on the server",
"incorrect reply handler",
"wrong poller operation",
"invalid poller type",
"invalid compressed data",
"invalid pseudo object",
"unsuitable GIOP version",
"per thread timeout with no current thread",
"HTTP buffer full",
"invalid HTTP header",
"invalid HTTP chunk",
"renegociation needed",
"invalid base64 data",
"invalid encrypted data",
"not initialised",
"invalid file",
"invalid key",
"unknown client",
"the proxy requires authentication",
"invalid GIOP message",
];
const JACORB_NO_PERMISSION_MINOR_CODES: &[&str] = &[
"the client side of the security attribute service failed",
"the authorization token layer acquisition service failed",
"the target side of the security attribute service failed",
];
fn standard_exception(id: &str) -> Option<&'static StandardException> {
STANDARD_EXCEPTIONS.iter().find(|standard| standard.id == id)
}
fn minor_code(codes: &'static [&'static str], code: u32) -> Option<&'static str> {
let index = usize::try_from(code).ok()?.checked_sub(1)?;
codes.get(index).copied()
}
fn no_header() -> CdrReader<'static> {
CdrReader::empty()
}
fn message_header<'a>(msg: &CdrReader<'a>, version: Version) -> Result<CdrReader<'a>> {
let body_at = body_offset(msg, version)?;
Ok(msg.substream(&msg.buffer()[..body_at as usize]))
}
fn body_length(msg: &CdrReader<'_>, version: Version) -> Result<u32> {
let body_at = body_offset(msg, version)?;
offset(msg.buffer().len() - body_at as usize)
}
fn message_reader<'a>(msg: &CdrReader<'a>, endian: Endian, at: usize) -> CdrReader<'a> {
let mut reader = msg.clone().with_endian(endian);
reader.set_position(at);
reader
}
fn body_offset(msg: &CdrReader<'_>, version: Version) -> Result<u32> {
let at = msg.position();
let end = msg.buffer().len();
if version.has_v12_layout() && at < end {
return offset(at.next_multiple_of(BODY_ALIGN).min(end));
}
offset(at)
}
fn write_body_padding(writer: &mut CdrWriter, version: Version) -> Result<()> {
if !version.has_v12_layout() {
return Ok(());
}
debug_assert!(writer.position() - writer.origin() >= HEADER_LEN, "a GIOP body follows the header of its message");
writer.align(BODY_ALIGN)
}
fn write_header(writer: &mut CdrWriter, version: Version, kind: u8, fragment_follows: bool) -> Result<()> {
debug_assert_eq!(writer.position(), writer.origin(), "a GIOP message starts the stream it is written into");
if kind == KIND_FRAGMENT && !version.has_fragments() {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Cannot write a fragment with GIOP {version}")));
}
if fragment_follows && !version.has_fragments() {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Cannot announce a fragment with GIOP {version}")));
}
let mut header = [0u8; HEADER_LEN_AT];
header[0..4].copy_from_slice(MAGIC);
header[4] = MAJOR;
header[5] = version.minor();
header[6] = Endian::NATIVE.is_little() as u8 | (fragment_follows as u8) << 1;
header[7] = kind;
writer.write_octet_slice(&header)?;
writer.write_ulong(0)
}
fn read_target<'d>(reader: &mut CdrReader<'d>, version: Version) -> Result<Target<'d>> {
if version.has_v12_layout() {
reader.read()
} else {
Ok(Target::Key(Cow::Borrowed(reader.read::<&[u8]>()?)))
}
}
fn write_target(writer: &mut CdrWriter, target: &Target<'_>, version: Version) -> Result<()> {
if version.has_v12_layout() {
return writer.write(target);
}
let key = target.key()
.ok_or_else(|| Error::new(ErrorKind::InvalidData)
.with_description(format!("GIOP {version} addresses an object by key only, not by {:?}", target.addressing())))?;
writer.write::<[u8]>(key)
}
fn offset(pos: usize) -> Result<u32> {
u32::try_from(pos)
.map_err(|_| Error::new(ErrorKind::InvalidData)
.with_description("A GIOP message is too long for an offset in it to be encoded"))
}
#[cfg(test)]
mod tests {
use super::*;
const VERSIONS: [Version; 4] = [Version::V10, Version::V11, Version::V12, Version::V13];
fn encode(msg: &Message<'_>, body: &[u8]) -> Buf<u8, 1> {
let mut writer = CdrWriter::new();
writer.write(msg).unwrap();
writer.write_octet_slice(body).unwrap();
writer.write(&MessageLen).unwrap();
writer.into_buf().freeze()
}
fn read_body(octets: &Buf<u8, 1>, at: usize, endian: Endian) -> CdrReader<'_> {
let mut reader = CdrReader::new(octets).with_endian(endian);
reader.set_position(at);
reader
}
fn decode_at(octets: &Buf<u8, 1>) -> (Message<'_>, usize) {
let mut reader = CdrReader::new(octets);
let message = reader.read().unwrap();
let at = reader.position();
(message, at)
}
fn decode(octets: &Buf<u8, 1>) -> Message<'_> {
decode_at(octets).0
}
fn contexts() -> [ServiceContext; 3] {
[
ServiceContext::new(15, &[0u8, 0xAA, 0xAA, 0xAA, 1, 2, 3, 4][..]),
ServiceContext::new(1, &[0u8][..]),
ServiceContext::new(42, &[0u8, b'x'][..]),
]
}
fn ids(contexts: ServiceContextIter<'_>) -> Vec<u32> {
contexts.map(|context| context.unwrap().id()).collect()
}
fn header(minor: u8, flags: u8, kind: u8) -> [u8; HEADER_LEN] {
let mut header = [0u8; HEADER_LEN];
header[0..4].copy_from_slice(MAGIC);
header[4] = MAJOR;
header[5] = minor;
header[6] = flags;
header[7] = kind;
header
}
#[test]
fn incomplete() {
let octets = encode(&Message::Request(
Request::new(Version::V11, 1, Target::new_key(&b"k"[..]), b"op")), b"\xDE");
let total_len = octets.len();
for len in 0..total_len {
let octets = Buf::from(&octets.as_slice()[..len]);
let msg = decode(&octets);
let expected = if len < HEADER_LEN { HEADER_LEN } else { total_len };
assert_eq!(msg.incomplete_len(), Some(expected), "at {len} octets");
assert_eq!(msg.version(), None);
}
let msg = decode(&octets);
assert_eq!(msg.incomplete_len(), None);
assert_eq!(msg.request_id(), Some(1));
}
#[test]
fn bad_header() {
assert!(Message::read(&mut CdrReader::new(&Buf::from(&b"XIOP\x01\x00\x00\x05\x00\x00\x00\x00"[..]))).is_err());
assert!(Message::read(&mut CdrReader::new(&Buf::from(&header(0, 0b10, 5)[..]))).is_err());
assert!(Message::read(&mut CdrReader::new(&Buf::from(&header(1, 0b10, 5)[..]))).is_ok());
assert!(Message::read(&mut CdrReader::new(&Buf::from(&header(1, 0b100, 5)[..]))).is_err());
}
#[test]
fn refused_header() {
assert!(matches!(Message::read(&mut CdrReader::new(&Buf::from(&b"GIOP\x02\x00\x00\x05\x00\x00\x00\x00"[..]))),
Ok(Message::InvalidVersion(2, 0))));
assert!(matches!(Message::read(&mut CdrReader::new(&Buf::from(&header(4, 0, 5)[..]))),
Ok(Message::InvalidVersion(MAJOR, 4))));
assert!(matches!(Message::read(&mut CdrReader::new(&Buf::from(&header(1, 0, 8)[..]))),
Ok(Message::InvalidKind(Version::V11, 8))));
assert!(matches!(Message::read(&mut CdrReader::new(&Buf::from(&header(0, 0, 7)[..]))),
Ok(Message::InvalidKind(Version::V10, KIND_FRAGMENT))));
assert!(matches!(Message::read(&mut CdrReader::new(&Buf::from(&header(0, 0, 5)[..]))),
Ok(Message::CloseConnection(Version::V10))));
assert!(matches!(Message::read(&mut CdrReader::new(&Buf::from(&header(1, 0, 7)[..]))),
Ok(Message::Fragment(_))));
}
#[test]
fn request_roundtrip() {
for version in VERSIONS {
let contexts = contexts();
let octets = encode(&Message::Request(
Request::new(version, 42, Target::new_key(&b"dev/key"[..]), b"read")
.with_service_contexts(&contexts)), b"\xDE\xAD");
let (message, body_at) = decode_at(&octets);
let Message::Request(request) = message else {
panic!("expected a request");
};
assert_eq!(request.version(), version);
assert_eq!(request.request_id(), 42);
assert!(request.response_expected());
assert_eq!(request.object(), Some(&b"dev/key"[..]));
assert_eq!(request.target().addressing(), Addressing::Key);
assert_eq!(request.operation(), b"read");
assert_eq!(ids(request.service_contexts()), [15, 1, 42]);
assert_eq!(request.body_len(), 2);
assert_eq!(body_at + request.body_len(), octets.as_bytes().len());
let mut body = read_body(&octets, body_at, request.endian());
assert_eq!(body.read_octet().unwrap(), 0xDE);
assert_eq!(body.read_octet().unwrap(), 0xAD);
assert!(body.is_empty());
let again = encode(&Message::Request(request), b"\xDE\xAD");
assert_eq!(again.as_bytes(), octets.as_bytes(), "{version}");
}
}
#[test]
fn other_kinds_roundtrip() {
let cases = [
Message::Reply(Reply::new(Version::V12, 8, ReplyStatus::SystemException)),
Message::CancelRequest(CancelRequest::new(Version::V10, 5)),
Message::LocateRequest(LocateRequest::new(Version::V12, 6, Target::new_key(&b"k"[..]))),
Message::LocateReply(LocateReply::new(Version::V13, 7, LocateStatus::ObjectHere)),
Message::CloseConnection(Version::V11),
Message::MessageError(Version::V10),
Message::Fragment(Fragment::new(Version::V12, 9).with_fragment_follows(true)),
];
for case in cases {
let octets = encode(&case, b"");
let back = decode(&octets);
assert_eq!(back.version(), case.version(), "{case:?}");
assert_eq!(back.request_id(), case.request_id(), "{case:?}");
assert_eq!(back.fragment_follows(), case.fragment_follows(), "{case:?}");
let mut writer = CdrWriter::new();
writer.write(&back).unwrap();
writer.write(&MessageLen).unwrap();
assert_eq!(writer.into_buf().as_bytes(), octets.as_bytes(), "{case:?}");
}
}
#[test]
fn fragment_data() {
let octets = encode(&Message::Fragment(
Fragment::new(Version::V12, 9).with_fragment_follows(true)), b"\x01\x02\x03\x04");
let (message, at) = decode_at(&octets);
let Message::Fragment(frag) = message else {
panic!("expected a fragment");
};
assert_eq!(frag.request_id(), Some(9));
assert_eq!(frag.body_len(), 4);
assert_eq!(at, HEADER_LEN + 4);
assert_eq!(&octets.as_bytes()[at..at + frag.body_len()], b"\x01\x02\x03\x04");
}
#[test]
fn body_padding() {
for version in [Version::V12, Version::V13] {
let octets = encode(&Message::Request(
Request::new(version, 1, Target::new_key(&b"k"[..]), b"op")), b"\xDE");
let (message, body_at) = decode_at(&octets);
let Message::Request(request) = message else {
panic!("expected a request");
};
assert_eq!(body_at % BODY_ALIGN, 0);
assert_eq!(request.body_len(), 1);
let mut body = read_body(&octets, body_at, request.endian());
assert_eq!(body.read_octet().unwrap(), 0xDE);
let octets = encode(&Message::Request(
Request::new(version, 1, Target::new_key(&b"k"[..]), b"op")), b"");
let (message, body_at) = decode_at(&octets);
let Message::Request(request) = message else {
panic!("expected a request");
};
assert_eq!(request.body_len(), 0);
assert_eq!(body_at, octets.as_bytes().len());
}
}
#[test]
fn lazy_service_contexts() {
let contexts = contexts();
let octets = encode(&Message::Reply(
Reply::new(Version::V12, 1, ReplyStatus::NoException)
.with_service_contexts(&contexts)), b"");
let Message::Reply(reply) = decode(&octets) else {
panic!("expected a reply");
};
assert_eq!(ids(reply.service_contexts()), [15, 1, 42]);
assert_eq!(ids(reply.service_contexts()), [15, 1, 42]);
let first = reply.service_contexts().next().unwrap().unwrap();
assert_eq!(first.id(), 15);
assert_eq!(first.reader().unwrap().read_ulong().unwrap(), 0x0102_0304);
let mut broken = octets.as_bytes().to_vec();
broken[20..24].copy_from_slice(&0xFFFFu32.to_ne_bytes());
assert!(Message::read(&mut CdrReader::new(&Buf::from(&broken[..]))).is_err());
}
#[test]
fn v12_only_status() {
let mut writer = CdrWriter::new();
assert!(writer.write(&Message::Reply(
Reply::new(Version::V10, 1, ReplyStatus::LocationForwardPerm))).is_err());
let mut writer = CdrWriter::new();
assert!(writer.write(&Message::LocateReply(
LocateReply::new(Version::V11, 1, LocateStatus::ObjectForwardPerm))).is_err());
let octets = encode(&Message::Reply(
Reply::new(Version::V10, 1, ReplyStatus::NoException)), b"");
let mut broken = octets.as_bytes().to_vec();
broken[20..24].copy_from_slice(&(ReplyStatus::LocationForwardPerm as u32).to_ne_bytes());
assert!(Message::read(&mut CdrReader::new(&Buf::from(&broken[..]))).is_err());
}
#[test]
fn pre_v12_key_addressing() {
let key = [0u8, 0, 0, 0];
let octets = encode(&Message::Request(
Request::new(Version::V11, 1, Target::new_key(&key[..]), b"op")), b"");
let Message::Request(request) = decode(&octets) else {
panic!("expected a request");
};
assert_eq!(request.object(), Some(&key[..]));
let profile = Target::Profile(Profile { tag: 0, data: Buf::from(&b"\x00p"[..]) });
let mut writer = CdrWriter::new();
assert!(writer.write(&Message::LocateRequest(
LocateRequest::new(Version::V11, 1, profile))).is_err());
}
#[test]
fn foreign_endian() {
let other = |value: u32| if Endian::NATIVE.is_little() {
value.to_be_bytes()
} else {
value.to_le_bytes()
};
let mut octets = header(0, (!Endian::NATIVE.is_little()) as u8, KIND_REPLY).to_vec();
octets.extend_from_slice(&other(0)); octets.extend_from_slice(&other(77)); octets.extend_from_slice(&other(0)); octets.extend_from_slice(&other(0x0102_0304));
let len = (octets.len() - HEADER_LEN) as u32;
octets[8..12].copy_from_slice(&other(len));
let octets = Buf::from(&octets[..]);
let (message, body_at) = decode_at(&octets);
let Message::Reply(reply) = message else {
panic!("expected a reply");
};
assert_eq!(reply.request_id(), 77);
assert_eq!(reply.status(), ReplyStatus::NoException);
let mut body = read_body(&octets, body_at, reply.endian());
assert_eq!(body.read_ulong().unwrap(), 0x0102_0304);
let mut writer = CdrWriter::new();
writer.write(&Message::Reply(reply)).unwrap();
writer.write(&MessageLen).unwrap();
let again = writer.into_buf();
assert_eq!(again.as_bytes()[FLAGS_AT] & 0b01, Endian::NATIVE.is_little() as u8);
}
#[test]
fn standard_ids() {
for (at, standard) in STANDARD_EXCEPTIONS.iter().enumerate() {
assert_eq!(standard.id, format!("IDL:omg.org/CORBA/{}:1.0", standard.name));
assert!(!STANDARD_EXCEPTIONS[..at].iter().any(|other| other.id == standard.id), "{}", standard.name);
assert!(standard.minor_codes.iter().all(|what| !what.is_empty()), "{}", standard.name);
}
}
#[test]
fn minor_code_decoding() {
let raised = |id, minor| SystemException::new(id).with_minor_code(minor);
let closed = raised(exception::TRANSIENT, OMNIORB_VMCID | 17);
assert_eq!(closed.name(), Some("TRANSIENT"));
assert_eq!(closed.vendor_id(), OMNIORB_VMCID);
assert_eq!(closed.vendor(), Some("omniORB"));
assert_eq!(closed.vendor_code(), 17);
assert_eq!(closed.description(), Some("connection closed"));
let factory = raised(exception::BAD_PARAM, OMG_VMCID | 1);
assert_eq!(factory.vendor(), Some("OMG"));
assert_eq!(factory.description(), Some("failure to register, unregister or look up a value factory"));
assert_eq!(raised(exception::COMM_FAILURE, OMG_VMCID | 1).description(), None);
let refused = raised(exception::NO_PERMISSION, JACORB_VMCID | 2);
assert_eq!(refused.vendor(), Some("JacORB"));
assert_eq!(refused.description(), Some("the authorization token layer acquisition service failed"));
let elsewhere = raised(exception::MARSHAL, JACORB_VMCID | 2);
assert_eq!(elsewhere.vendor(), Some("JacORB"), "the vendor is named whatever the exception");
assert_eq!(elsewhere.description(), None);
let foreign = raised(exception::INTERNAL, 0x1234_5678);
assert_eq!(foreign.vendor_id(), 0x1234_5000);
assert_eq!(foreign.vendor_code(), 0x678);
assert_eq!(foreign.vendor(), None);
assert_eq!(foreign.description(), None);
let user = raised("IDL:Tango/DevFailed:1.0", OMG_VMCID | 1);
assert_eq!(user.name(), None);
assert_eq!(user.description(), None);
}
#[test]
fn exception_display() {
let raised = |id, minor| SystemException::new(id)
.with_minor_code(minor)
.with_completion_status(CompletionStatus::Maybe);
assert_eq!(raised(exception::COMM_FAILURE, OMNIORB_VMCID | 17).to_string(),
"COMM_FAILURE (IDL:omg.org/CORBA/COMM_FAILURE:1.0), minor 0x41540011 (omniORB 17: connection closed), completed MAYBE");
assert_eq!(raised(exception::COMM_FAILURE, OMG_VMCID | 1).to_string(),
"COMM_FAILURE (IDL:omg.org/CORBA/COMM_FAILURE:1.0), minor 0x4F4D0001 (OMG 1), completed MAYBE");
assert_eq!(raised(exception::NO_PERMISSION, JACORB_VMCID | 1).to_string(),
"NO_PERMISSION (IDL:omg.org/CORBA/NO_PERMISSION:1.0), minor 0x4A430001 (JacORB 1: the client side of the security attribute service failed), completed MAYBE");
assert_eq!(raised("IDL:Tango/DevFailed:1.0", 0x1234_5678).to_string(),
"IDL:Tango/DevFailed:1.0, minor 0x12345678, completed MAYBE");
assert_eq!(SystemException::new(exception::OBJECT_NOT_EXIST).to_string(),
"OBJECT_NOT_EXIST (IDL:omg.org/CORBA/OBJECT_NOT_EXIST:1.0), completed NO");
let raised = raised(exception::MARSHAL, OMG_VMCID | 4);
let debug = format!("{raised:?}");
for part in [
"SystemException {",
"name: Some(\"MARSHAL\")",
"minor_code: 0x4F4D0004",
"vendor: Some(\"OMG\")",
"vendor_code: 4",
"description: Some(",
"completion_status: Maybe",
] {
assert!(debug.contains(part), "{part} is missing from {debug}");
}
}
#[test]
fn minor_code_tables() {
let fits = |codes: &[&str]| codes.len() as u32 <= !VMCID_MASK;
assert!(fits(OMNIORB_MINOR_CODES));
assert!(fits(JACORB_NO_PERMISSION_MINOR_CODES));
assert!(STANDARD_EXCEPTIONS.iter().all(|standard| fits(standard.minor_codes)));
let vendors = [OMG_VMCID, OMNIORB_VMCID, JACORB_VMCID];
assert!(vendors.iter().all(|vmcid| vmcid & !VMCID_MASK == 0));
assert!(vendors.iter().enumerate().all(|(at, vmcid)| !vendors[..at].contains(vmcid)));
}
}