use std::borrow::Cow;
use std::fmt::Debug;
use crate::error::{Error, ErrorKind, Result};
use crate::buf::Buf;
use super::cdr::{CdrReader, CdrWriter, CdrRead, CdrWrite};
use super::codeset::{Codeset, WideCodeset, AnyCodeset};
use super::giop::Version;
#[derive(Debug, Clone)]
pub struct Ior<'a> {
pub type_id: Cow<'a, str>,
pub profiles: Cow<'a, [Profile]>,
}
impl<'d> CdrRead<'d> for Ior<'d> {
fn read(buf: &mut CdrReader<'d>) -> Result<Self> {
Ok(Self {
type_id: buf.read_string()?,
profiles: Cow::Owned(buf.read()?),
})
}
}
impl CdrWrite for Ior<'_> {
fn write(&self, buf: &mut CdrWriter) -> Result<()> {
buf.write_string(&self.type_id)?;
buf.write(&*self.profiles)?;
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct Profile {
pub tag: u32,
pub data: Buf<u8, 1>,
}
impl CdrRead<'_> for Profile {
fn read(buf: &mut CdrReader<'_>) -> Result<Self> {
Ok(Self {
tag: buf.read_ulong()?,
data: buf.read()?,
})
}
}
impl CdrWrite for Profile {
fn write(&self, buf: &mut CdrWriter) -> Result<()> {
buf.write_ulong(self.tag)?;
buf.write(&self.data)?;
Ok(())
}
}
impl Profile {
pub fn reader(&self) -> Result<CdrReader<'_>> {
let mut reader = CdrReader::new(&self.data);
reader.read_endian()?;
Ok(reader)
}
pub fn parse(&self) -> Result<Option<ProfileKind<'_>>> {
let mut reader = self.reader()?;
Ok(Some(match self.tag {
0 => ProfileKind::Iiop(reader.read()?),
1 => ProfileKind::MultipleComponents(Cow::Owned(reader.read()?)),
_ => return Ok(None),
}))
}
}
#[derive(Debug, Clone)]
pub enum ProfileKind<'a> {
Iiop(IiopProfile<'a>),
MultipleComponents(Cow<'a, [Component]>),
}
impl ProfileKind<'_> {
pub fn build(&self) -> Result<Profile> {
let mut writer = CdrWriter::new();
writer.write_endian()?;
let tag = match self {
ProfileKind::Iiop(iiop) => {
writer.write(iiop)?;
0
}
ProfileKind::MultipleComponents(components) => {
writer.write(&**components)?;
1
}
};
Ok(Profile {
tag,
data: writer.into_buf().freeze(),
})
}
}
#[derive(Debug, Clone)]
pub struct IiopProfile<'a> {
pub version: Version,
pub host: Cow<'a, str>,
pub port: u16,
pub object_key: Cow<'a, [u8]>,
pub components: Cow<'a, [Component]>,
}
impl<'d> CdrRead<'d> for IiopProfile<'d> {
fn read(buf: &mut CdrReader<'d>) -> Result<Self> {
let major = buf.read_octet()?;
let minor = buf.read_octet()?;
let Some(version) = Version::from_minor(minor).filter(|_| major == 1) else {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Invalid version for IIOP: {major}.{minor}")));
};
Ok(Self {
version,
host: buf.read_string()?,
port: buf.read_ushort()?,
object_key: Cow::Borrowed(buf.read::<&[u8]>()?),
components: if version == Version::V10 {
Cow::Borrowed(&[])
} else {
Cow::Owned(buf.read::<Vec<_>>()?)
}
})
}
}
impl CdrWrite for IiopProfile<'_> {
fn write(&self, buf: &mut CdrWriter) -> Result<()> {
buf.write_octet(1)?;
buf.write_octet(self.version.minor())?;
buf.write_string(&self.host)?;
buf.write_ushort(self.port)?;
buf.write::<[u8]>(&self.object_key)?;
if self.version == Version::V10 {
if !self.components.is_empty() {
return Err(Error::new(ErrorKind::InvalidData)
.with_description("Cannot encode IIOP profile components with version 1.0"));
}
} else {
buf.write(&*self.components)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct Component {
pub tag: u32,
pub data: Buf<u8, 1>,
}
impl CdrRead<'_> for Component {
fn read(buf: &mut CdrReader<'_>) -> Result<Self> {
Ok(Self {
tag: buf.read_ulong()?,
data: buf.read()?,
})
}
}
impl CdrWrite for Component {
fn write(&self, buf: &mut CdrWriter) -> Result<()> {
buf.write_ulong(self.tag)?;
buf.write(&self.data)?;
Ok(())
}
}
impl Component {
pub fn reader(&self) -> Result<CdrReader<'_>> {
let mut reader = CdrReader::new(&self.data);
reader.read_endian()?;
Ok(reader)
}
pub fn parse(&self) -> Result<Option<ComponentKind<'_>>> {
let mut reader = self.reader()?;
Ok(Some(match self.tag {
0 => ComponentKind::OrbType(reader.read_ulong()?),
1 => ComponentKind::Codesets(reader.read()?),
3 => ComponentKind::AltIiopAddr(reader.read()?),
_ => return Ok(None),
}))
}
}
#[derive(Debug, Clone)]
pub enum ComponentKind<'a> {
OrbType(u32),
Codesets(CodesetsComponent<'a>),
AltIiopAddr(AltIiopAddr<'a>),
}
impl ComponentKind<'_> {
pub fn build(&self) -> Result<Component> {
let mut writer = CdrWriter::new();
writer.write_endian()?;
let tag = match self {
ComponentKind::OrbType(orb_type) => {
writer.write_ulong(*orb_type)?;
0
}
ComponentKind::Codesets(codesets) => {
writer.write(codesets)?;
1
}
ComponentKind::AltIiopAddr(alt_addr) => {
writer.write(alt_addr)?;
3
}
};
Ok(Component {
tag,
data: writer.into_buf().freeze(),
})
}
}
#[derive(Debug, Clone)]
pub struct CodesetsComponent<'a> {
pub native_codeset: u32,
pub conversion_codesets: Cow<'a, [u32]>,
pub native_wide_codeset: u32,
pub conversion_wide_codesets: Cow<'a, [u32]>,
}
impl<'d> CdrRead<'d> for CodesetsComponent<'d> {
fn read(buf: &mut CdrReader<'d>) -> Result<Self> {
Ok(Self {
native_codeset: buf.read()?,
conversion_codesets: buf.read()?,
native_wide_codeset: buf.read()?,
conversion_wide_codesets: buf.read()?,
})
}
}
impl CdrWrite for CodesetsComponent<'_> {
fn write(&self, buf: &mut CdrWriter) -> Result<()> {
buf.write_ulong(self.native_codeset)?;
buf.write(&*self.conversion_codesets)?;
buf.write_ulong(self.native_wide_codeset)?;
buf.write(&*self.conversion_wide_codesets)?;
Ok(())
}
}
impl CodesetsComponent<'_> {
pub fn parse_native_codeset(&self) -> Option<Codeset> {
Codeset::from_id(self.native_codeset)
}
pub fn parse_conversion_codesets(&self) -> impl Iterator<Item = Codeset> + use<'_> {
self.conversion_codesets.iter().copied().filter_map(Codeset::from_id)
}
pub fn parse_native_wide_codeset(&self) -> Option<WideCodeset> {
WideCodeset::from_id(self.native_wide_codeset)
}
pub fn parse_conversion_wide_codesets(&self) -> impl Iterator<Item = WideCodeset> + use<'_> {
self.conversion_wide_codesets.iter().copied().filter_map(WideCodeset::from_id)
}
}
#[derive(Debug, Clone)]
pub struct AltIiopAddr<'a> {
pub host: Cow<'a, str>,
pub port: u16,
}
impl<'d> CdrRead<'d> for AltIiopAddr<'d> {
fn read(buf: &mut CdrReader<'d>) -> Result<Self> {
Ok(Self {
host: buf.read_string()?,
port: buf.read()?,
})
}
}
impl CdrWrite for AltIiopAddr<'_> {
fn write(&self, buf: &mut CdrWriter) -> Result<()> {
buf.write_string(&self.host)?;
buf.write_ushort(self.port)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip() {
let codesets = ComponentKind::Codesets(CodesetsComponent {
native_codeset: Codeset::Utf8.id(),
conversion_codesets: Cow::Borrowed(&[]),
native_wide_codeset: WideCodeset::Utf16.id(),
conversion_wide_codesets: Cow::Borrowed(&[]),
}).build().unwrap();
assert_eq!(codesets.tag, 1);
let profile = ProfileKind::Iiop(IiopProfile {
version: Version::V12,
host: Cow::Borrowed("localhost"),
port: 10000,
object_key: Cow::Borrowed(b"the object key"),
components: Cow::Owned(vec![codesets]),
}).build().unwrap();
assert_eq!(profile.tag, 0);
let mut writer = CdrWriter::new();
writer.write(&Ior {
type_id: Cow::Borrowed("IDL:Tango/Device_5:1.0"),
profiles: Cow::Owned(vec![profile]),
}).unwrap();
let buf = writer.into_buf().freeze();
let ior = CdrReader::new(&buf).read::<Ior>().unwrap();
assert_eq!(ior.type_id, "IDL:Tango/Device_5:1.0");
assert_eq!(ior.profiles.len(), 1);
let Some(ProfileKind::Iiop(iiop)) = ior.profiles[0].parse().unwrap() else {
panic!("expected an IIOP profile");
};
assert_eq!(iiop.version, Version::V12);
assert_eq!(iiop.host, "localhost");
assert_eq!(iiop.port, 10000);
assert_eq!(&*iiop.object_key, b"the object key");
assert_eq!(iiop.components.len(), 1);
let Some(ComponentKind::Codesets(codesets)) = iiop.components[0].parse().unwrap() else {
panic!("expected the codesets component");
};
assert_eq!(codesets.parse_native_codeset(), Some(Codeset::Utf8));
assert_eq!(codesets.parse_native_wide_codeset(), Some(WideCodeset::Utf16));
assert_eq!(codesets.parse_conversion_codesets().count(), 0);
}
#[test]
fn unknown_component() {
let mut component = ComponentKind::OrbType(0x41545400).build().unwrap();
component.tag = 0xDEAD;
assert!(component.parse().unwrap().is_none());
}
#[test]
fn v10_no_components() {
let mut iiop = IiopProfile {
version: Version::V10,
host: Cow::Borrowed("localhost"),
port: 10000,
object_key: Cow::Borrowed(b"the object key"),
components: Cow::Owned(vec![ComponentKind::OrbType(0x41545400).build().unwrap()]),
};
assert!(ProfileKind::Iiop(iiop.clone()).build().is_err());
iiop.components = Cow::Borrowed(&[]);
assert!(ProfileKind::Iiop(iiop).build().is_ok());
}
}