use crate::{
protocol::{util, util_sync},
HdbError, HdbResult, HdbValue, TypeId,
};
use byteorder::{LittleEndian, ReadBytesExt};
#[derive(Debug, Default)]
pub struct ParameterDescriptors(Vec<ParameterDescriptor>);
impl ParameterDescriptors {
pub fn iter_in(&self) -> impl std::iter::Iterator<Item = &ParameterDescriptor> {
self.0.iter().filter(|ms| {
(ms.direction == ParameterDirection::IN) | (ms.direction == ParameterDirection::INOUT)
})
}
pub fn iter_out(&self) -> impl std::iter::Iterator<Item = &ParameterDescriptor> {
self.0.iter().filter(|ms| {
(ms.direction == ParameterDirection::OUT) | (ms.direction == ParameterDirection::INOUT)
})
}
pub fn has_in(&self) -> bool {
self.iter_in().next().is_some()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub(crate) fn parse(count: usize, rdr: &mut dyn std::io::Read) -> HdbResult<Self> {
let mut vec_pd = Vec::<ParameterDescriptor>::new();
let mut name_offsets = Vec::<u32>::new();
for _ in 0..count {
let option = rdr.read_u8()?;
let value_type = rdr.read_u8()?;
let mode = ParameterDescriptor::direction_from_u8(rdr.read_u8()?)?;
rdr.read_u8()?;
name_offsets.push(rdr.read_u32::<LittleEndian>()?);
let length = rdr.read_i16::<LittleEndian>()?;
let fraction = rdr.read_i16::<LittleEndian>()?;
rdr.read_u32::<LittleEndian>()?;
vec_pd.push(ParameterDescriptor::try_new(
option, value_type, mode, length, fraction,
)?);
}
for (descriptor, name_offset) in vec_pd.iter_mut().zip(name_offsets.iter()) {
if name_offset != &u32::max_value() {
let length = rdr.read_u8()?;
let name = util::string_from_cesu8(util_sync::parse_bytes(length as usize, rdr)?)?;
descriptor.set_name(name);
}
}
Ok(Self(vec_pd))
}
}
impl std::ops::Index<usize> for ParameterDescriptors {
type Output = ParameterDescriptor;
fn index(&self, index: usize) -> &Self::Output {
self.0.index(index)
}
}
#[derive(Clone, Debug)]
pub struct ParameterDescriptor {
name: Option<String>,
type_id: TypeId,
binding: ParameterBinding,
scale: i16,
precision: i16,
direction: ParameterDirection,
auto_incremented: bool,
array_type: bool,
}
impl ParameterDescriptor {
fn try_new(
parameter_option: u8,
type_code: u8,
direction: ParameterDirection,
precision: i16,
scale: i16,
) -> HdbResult<Self> {
let type_id = TypeId::try_new(type_code)?;
let (binding, auto_incremented, array_type) = evaluate_option(parameter_option);
Ok(Self {
binding,
type_id,
direction,
precision,
scale,
name: None,
auto_incremented,
array_type,
})
}
pub fn binding(&self) -> ParameterBinding {
self.binding
}
pub fn is_nullable(&self) -> bool {
matches!(self.binding, ParameterBinding::Optional)
}
pub fn has_default(&self) -> bool {
matches!(self.binding, ParameterBinding::HasDefault)
}
pub fn is_auto_incremented(&self) -> bool {
self.auto_incremented
}
pub fn is_array_type(&self) -> bool {
self.array_type
}
pub fn type_id(&self) -> TypeId {
self.type_id
}
pub fn scale(&self) -> i16 {
self.scale
}
pub fn precision(&self) -> i16 {
self.precision
}
pub fn direction(&self) -> ParameterDirection {
self.direction.clone()
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
fn set_name(&mut self, name: String) {
self.name = Some(name);
}
fn direction_from_u8(v: u8) -> HdbResult<ParameterDirection> {
match v {
1 => Ok(ParameterDirection::IN),
2 => Ok(ParameterDirection::INOUT),
4 => Ok(ParameterDirection::OUT),
_ => Err(HdbError::ImplDetailed(format!(
"invalid value for ParameterDirection: {v}"
))),
}
}
pub fn parse_value<S: AsRef<str>>(&self, s: S) -> HdbResult<HdbValue<'static>> {
Ok(serde_db::ser::DbvFactory::serialize_str(&self, s.as_ref())?)
}
}
fn evaluate_option(parameter_option: u8) -> (ParameterBinding, bool, bool) {
(
if parameter_option & 0b_0000_0001_u8 > 0 {
ParameterBinding::Mandatory
} else if parameter_option & 0b_0000_0010_u8 > 0 {
ParameterBinding::Optional
} else {
if parameter_option & 0b_0000_0010_u8 == 0 {
log::warn!("ParameterDescriptor got invalid parameter_option, assuming HasDefault");
}
ParameterBinding::HasDefault
},
(parameter_option & 0b_0010_0000_u8) != 0,
(parameter_option & 0b_0100_0000_u8) != 0,
)
}
impl std::fmt::Display for ParameterDescriptor {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some(ref s) = self.name {
write!(fmt, "{s} ")?;
}
write!(
fmt,
"{:?} {:?} {:?}, Scale({}), Precision({})",
self.type_id,
self.binding(),
self.direction(),
self.precision(),
self.scale()
)?;
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ParameterBinding {
Optional,
Mandatory,
HasDefault,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ParameterDirection {
IN,
INOUT,
OUT,
}