use crate::value::{Spectrum, Value, ValueFormat};
use crate::error::{Error, ErrorKind, Result};
use crate::dev::DevState;
use super::super::orb::cdr::{CdrReader, CdrWriter};
use super::{
read_encoded, read_scalar, read_seq, read_spectrum, write_any_string_array_tc, write_array_tc,
write_encoded, write_encoded_tc, write_scalar_tc, write_seq, CdrTangoElement,
TK_ALIAS, TK_BOOL, TK_DOUBLE, TK_ENUM, TK_FLOAT, TK_LONG, TK_LONGLONG, TK_NULL, TK_OCTET,
TK_SHORT, TK_STRING, TK_STRUCT, TK_ULONG, TK_ULONGLONG, TK_USHORT,
};
pub fn write_command_data(buf: &mut CdrWriter, value: &Value) -> Result<()> {
fn format<T: CdrTangoElement + ?Sized>(buf: &mut CdrWriter, format: &ValueFormat<T>) -> Result<()> {
match format {
ValueFormat::Scalar(value) => {
write_scalar_tc(buf, T::TK)?;
T::write_values(buf, value)
}
ValueFormat::Spectrum(values) => {
write_array_tc(buf, T::TK, T::ARRAY_REPO_ID)?;
write_seq(buf, values)
}
ValueFormat::Image(_) => Err(Error::new(ErrorKind::InvalidData)
.with_description("Image data cannot be used as command parameter")),
}
}
fn long_double_string<T: CdrTangoElement>(buf: &mut CdrWriter, repo_id: &str, name: &str, values: &Spectrum<T>, strings: &Spectrum<str>) -> Result<()> {
write_any_string_array_tc(buf, repo_id, name, T::TK, T::ARRAY_REPO_ID)?;
write_seq(buf, values)?;
write_seq(buf, strings)
}
match value {
Value::Bool(values) => format(buf, values),
Value::Octet(values) => format(buf, values),
Value::UShort(values) => format(buf, values),
Value::Short(values) => format(buf, values),
Value::ULong(values) => format(buf, values),
Value::Long(values) => format(buf, values),
Value::ULong64(values) => format(buf, values),
Value::Long64(values) => format(buf, values),
Value::Float(values) => format(buf, values),
Value::Double(values) => format(buf, values),
Value::Enum(values) => format(buf, values),
Value::State(values) => format(buf, values),
Value::String(values) => format(buf, values),
Value::Void => write_scalar_tc(buf, TK_NULL),
Value::LongString(longs, strings) =>
long_double_string(buf, tango_repo_id!("DevVarLongStringArray"), "lvalue", longs, strings),
Value::DoubleString(doubles, strings) =>
long_double_string(buf, tango_repo_id!("DevVarDoubleStringArray"), "dvalue", doubles, strings),
Value::Encoded(encoded) => {
write_encoded_tc(buf)?;
write_encoded(buf, encoded)
}
}
}
pub fn read_command_data(buf: &mut CdrReader<'_>) -> Result<Value> {
Ok(match buf.read_ulong()? {
TK_NULL => Value::Void,
TK_BOOL => read_scalar::<bool>(buf)?,
TK_OCTET => read_scalar::<u8>(buf)?,
TK_SHORT => read_scalar::<i16>(buf)?,
TK_USHORT => read_scalar::<u16>(buf)?,
TK_LONG => read_scalar::<i32>(buf)?,
TK_ULONG => read_scalar::<u32>(buf)?,
TK_LONGLONG => read_scalar::<i64>(buf)?,
TK_ULONGLONG => read_scalar::<u64>(buf)?,
TK_FLOAT => read_scalar::<f32>(buf)?,
TK_DOUBLE => read_scalar::<f64>(buf)?,
TK_STRING => {
let _max_len = buf.read_ulong()?;
read_scalar::<str>(buf)?
}
TK_ENUM => {
let repo_id = buf.read_encapsulation()?.read_string()?;
if repo_id != tango_repo_id!("DevState") {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Enum repository ID {repo_id:?} is not accepted in Tango command data"))
.with_append_description("Expected: DevState"))
}
read_scalar::<DevState>(buf)?
}
TK_STRUCT => {
let repo_id = buf.read_encapsulation()?.read_string()?;
match &*repo_id {
tango_repo_id!("DevVarLongStringArray") => Value::LongString(read_seq(buf)?, read_seq(buf)?),
tango_repo_id!("DevVarDoubleStringArray") => Value::DoubleString(read_seq(buf)?, read_seq(buf)?),
tango_repo_id!("DevEncoded") => Value::Encoded(read_encoded(buf)?),
repo_id => return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Structure repository ID {repo_id:?} is not accepted in Tango command data"))
.with_append_description("Expected: DevVarLongStringArray, DevVarDoubleStringArray or DevEncoded")),
}
}
TK_ALIAS => {
let repo_id = buf.read_encapsulation()?.read_string()?;
match &*repo_id {
tango_repo_id!("DevVarBooleanArray") => read_spectrum::<bool>(buf)?,
tango_repo_id!("DevVarCharArray") => read_spectrum::<u8>(buf)?,
tango_repo_id!("DevVarUShortArray") => read_spectrum::<u16>(buf)?,
tango_repo_id!("DevVarShortArray") => read_spectrum::<i16>(buf)?,
tango_repo_id!("DevVarULongArray") => read_spectrum::<u32>(buf)?,
tango_repo_id!("DevVarLongArray") => read_spectrum::<i32>(buf)?,
tango_repo_id!("DevVarULong64Array") => read_spectrum::<u64>(buf)?,
tango_repo_id!("DevVarLong64Array") => read_spectrum::<i64>(buf)?,
tango_repo_id!("DevVarFloatArray") => read_spectrum::<f32>(buf)?,
tango_repo_id!("DevVarDoubleArray") => read_spectrum::<f64>(buf)?,
tango_repo_id!("DevVarStateArray") => read_spectrum::<DevState>(buf)?,
tango_repo_id!("DevVarStringArray") => read_spectrum::<str>(buf)?,
repo_id => return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Alias repository ID {repo_id:?} is not accepted in Tango command data"))
.with_append_description("Expected: DevVar*Array")),
}
}
tk => return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Typecode kind {tk} is not accepted in Tango command data"))),
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::buf::Buf;
use crate::value::DevEncoded;
use super::super::TK_SEQUENCE;
fn check_composite_typecode(reader: &mut CdrReader<'_>, repo_id: &str, first_name: &str, first_repo_id: &str) {
assert_eq!(reader.read_ulong().unwrap(), TK_STRUCT);
let mut param = reader.read_encapsulation().unwrap();
assert_eq!(param.read_string().unwrap(), repo_id);
assert_eq!(param.read_string().unwrap(), type_name(repo_id));
assert_eq!(param.read_sequence_len().unwrap(), 2);
assert_eq!(param.read_string().unwrap(), first_name);
check_array_typecode(&mut param, first_repo_id);
assert_eq!(param.read_string().unwrap(), "svalue");
check_array_typecode(&mut param, tango_repo_id!("DevVarStringArray"));
assert!(param.is_empty(), "the parameters of the structure hold more than the two members they declare");
}
fn check_array_typecode(reader: &mut CdrReader<'_>, repo_id: &str) {
assert_eq!(reader.read_ulong().unwrap(), TK_ALIAS);
let mut param = reader.read_encapsulation().unwrap();
assert_eq!(param.read_string().unwrap(), repo_id);
assert_eq!(param.read_string().unwrap(), type_name(repo_id));
assert_eq!(param.read_ulong().unwrap(), TK_SEQUENCE);
let mut sequence = param.read_encapsulation().unwrap();
if sequence.read_ulong().unwrap() == TK_STRING {
assert_eq!(sequence.read_ulong().unwrap(), 0, "the strings should be unbounded");
}
assert_eq!(sequence.read_ulong().unwrap(), 0, "the sequence should be unbounded");
assert!(sequence.is_empty(), "the parameters of the sequence hold more than an element type and a bound");
assert!(param.is_empty(), "the parameters of the alias hold more than a name and a type");
}
fn type_name(repo_id: &str) -> &str {
&repo_id[10..repo_id.len() - 4]
}
#[test]
fn long_string_array() {
let data = Value::from((Buf::from(&[1i32, 2, 3][..]), Buf::from(&["one", "two"][..])));
let mut writer = CdrWriter::new();
write_command_data(&mut writer, &data).unwrap();
let buf = writer.into_buf().freeze();
let mut reader = CdrReader::new(&buf);
check_composite_typecode(
&mut reader,
tango_repo_id!("DevVarLongStringArray"),
"lvalue",
tango_repo_id!("DevVarLongArray"));
assert_eq!(read_command_data(&mut CdrReader::new(&buf)).unwrap(), data);
}
#[test]
fn encoded_command() {
let data = Value::from(DevEncoded::new("JPEG_RGB", vec![0xFFu8, 0xD8, 0xFF]));
let mut writer = CdrWriter::new();
write_command_data(&mut writer, &data).unwrap();
let buf = writer.into_buf().freeze();
let mut reader = CdrReader::new(&buf);
assert_eq!(reader.read_ulong().unwrap(), TK_STRUCT);
let mut param = reader.read_encapsulation().unwrap();
assert_eq!(param.read_string().unwrap(), tango_repo_id!("DevEncoded"));
assert_eq!(param.read_string().unwrap(), "DevEncoded");
assert_eq!(param.read_sequence_len().unwrap(), 2);
assert_eq!(param.read_string().unwrap(), "encoded_format");
assert_eq!(param.read_ulong().unwrap(), TK_ALIAS);
let mut alias = param.read_encapsulation().unwrap();
assert_eq!(alias.read_string().unwrap(), tango_repo_id!("DevString"));
assert_eq!(alias.read_string().unwrap(), "DevString");
assert_eq!(alias.read_ulong().unwrap(), TK_STRING);
assert_eq!(alias.read_ulong().unwrap(), 0, "the string should be unbounded");
assert!(alias.is_empty(), "the parameters of the alias hold more than a name and a type");
assert_eq!(param.read_string().unwrap(), "encoded_data");
check_array_typecode(&mut param, tango_repo_id!("DevVarCharArray"));
assert!(param.is_empty(), "the parameters of the structure hold more than the two members they declare");
let back = read_command_data(&mut CdrReader::new(&buf)).unwrap();
assert!(!buf.is_unique(), "the value was copied out of the message");
assert_eq!(back, data);
}
#[test]
fn double_string_array() {
let data = Value::from((Buf::from(&[1.5f64, 2.5][..]), Buf::from(&["one"][..])));
let mut writer = CdrWriter::new();
write_command_data(&mut writer, &data).unwrap();
let buf = writer.into_buf().freeze();
let mut reader = CdrReader::new(&buf);
check_composite_typecode(
&mut reader,
tango_repo_id!("DevVarDoubleStringArray"),
"dvalue",
tango_repo_id!("DevVarDoubleArray"));
assert_eq!(read_command_data(&mut CdrReader::new(&buf)).unwrap(), data);
}
}