use std::iter::FusedIterator;
use std::ops::Range;
use crate::buffer::Buffer;
#[derive(Debug, thiserror::Error)]
pub enum FieldValueError<E> {
#[error("Missing field tag")]
Missing,
#[error("Invalid field value: {0}")]
Invalid(#[from] E),
}
impl<E> PartialEq<FieldValueError<E>> for FieldValueError<E> {
fn eq(&self, other: &FieldValueError<E>) -> bool {
matches!(
(self, other),
(FieldValueError::Missing, FieldValueError::Missing)
)
}
}
impl<E> From<Option<E>> for FieldValueError<E> {
fn from(e: Option<E>) -> Self {
match e {
Some(e) => FieldValueError::Invalid(e),
None => FieldValueError::Missing,
}
}
}
pub trait FieldType<'a>
where
Self: Sized,
{
type Error;
type SerializeSettings: Default;
#[inline]
fn serialize<B>(&self, buffer: &mut B) -> usize
where
B: Buffer,
{
self.serialize_with(buffer, Self::SerializeSettings::default())
}
fn serialize_with<B>(&self, buffer: &mut B, settings: Self::SerializeSettings) -> usize
where
B: Buffer;
fn deserialize(data: &'a [u8]) -> Result<Self, Self::Error>;
fn deserialize_lossy(data: &'a [u8]) -> Result<Self, Self::Error> {
Self::deserialize(data)
}
fn to_bytes(&self) -> Vec<u8> {
let mut buffer = Vec::new();
self.serialize(&mut buffer);
buffer
}
fn to_string(&self) -> String {
String::from_utf8(self.to_bytes()).expect("Invalid UTF-8 representation of FIX field.")
}
}
pub trait FieldMap<F> {
type Group: RepeatingGroup<Entry = Self>;
fn get_raw(&self, field: F) -> Option<&[u8]>;
fn group(&self, field: F) -> Result<Self::Group, FieldValueError<<usize as FieldType>::Error>>;
#[inline]
fn group_opt(&self, field: F) -> Result<Option<Self::Group>, <usize as FieldType>::Error> {
match self.group(field) {
Ok(group) => Ok(Some(group)),
Err(FieldValueError::Missing) => Ok(None),
Err(FieldValueError::Invalid(e)) => Err(e),
}
}
#[inline]
fn get<'a, V>(&'a self, field: F) -> Result<V, FieldValueError<V::Error>>
where
V: FieldType<'a>,
{
self.get_opt(field)
.map_err(FieldValueError::Invalid)
.and_then(|opt| opt.ok_or(FieldValueError::Missing))
}
#[inline]
fn get_lossy<'a, V>(&'a self, field: F) -> Result<V, FieldValueError<V::Error>>
where
V: FieldType<'a>,
{
self.get_lossy_opt(field)
.map_err(FieldValueError::Invalid)
.and_then(|opt| opt.ok_or(FieldValueError::Missing))
}
#[inline]
fn get_opt<'a, V>(&'a self, field: F) -> Result<Option<V>, V::Error>
where
V: FieldType<'a>,
{
self.get_raw(field).map(V::deserialize).transpose()
}
#[inline]
fn get_lossy_opt<'a, V>(&'a self, field: F) -> Result<Option<V>, V::Error>
where
V: FieldType<'a>,
{
self.get_raw(field).map(V::deserialize_lossy).transpose()
}
}
pub trait RepeatingGroup: Sized {
type Entry;
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn get(&self, i: usize) -> Option<Self::Entry>;
fn entries(&self) -> GroupEntries<Self> {
GroupEntries {
group: self,
range: 0..self.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct GroupEntries<'a, G> {
group: &'a G,
range: Range<usize>,
}
impl<'a, G> Iterator for GroupEntries<'a, G>
where
G: RepeatingGroup,
{
type Item = G::Entry;
fn next(&mut self) -> Option<Self::Item> {
let i = self.range.next()?;
self.group.get(i)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
}
}
impl<'a, G> FusedIterator for GroupEntries<'a, G> where G: RepeatingGroup {}
impl<'a, G> ExactSizeIterator for GroupEntries<'a, G> where G: RepeatingGroup {}
impl<'a, G> DoubleEndedIterator for GroupEntries<'a, G>
where
G: RepeatingGroup,
{
fn next_back(&mut self) -> Option<Self::Item> {
let i = self.range.next_back()?;
self.group.get(i)
}
}