use crate::string::SmartString;
use crate::{Datatype, Dictionary, FixDatatype, TagU32};
pub trait IsFieldDefinition {
fn tag(&self) -> TagU32;
fn name(&self) -> &str;
fn location(&self) -> FieldLocation;
}
#[derive(Debug, Copy, Clone)]
pub struct Field<'a>(&'a Dictionary, &'a FieldData);
#[derive(Clone, Debug)]
pub struct FieldData {
pub(crate) name: SmartString,
pub(crate) tag: u32,
pub(crate) data_type_name: SmartString,
pub(crate) associated_data_tag: Option<usize>,
pub(crate) value_restrictions: Option<Vec<FieldEnumData>>,
pub(crate) required: bool,
pub(crate) description: Option<String>,
}
#[derive(Clone, Debug)]
pub(crate) struct FieldEnumData {
pub(crate) value: String,
pub(crate) description: String,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct FieldEnum<'a>(&'a Dictionary, &'a FieldEnumData);
impl<'a> FieldEnum<'a> {
pub fn value(&self) -> &str {
&self.1.value[..]
}
pub fn description(&self) -> &str {
&self.1.description[..]
}
}
impl<'a> Field<'a> {
pub fn new(dictionary: &'a Dictionary, field_data: &'a FieldData) -> Self {
Self(dictionary, field_data)
}
pub fn doc_url_onixs(&self, version: &str) -> String {
let v = match version {
"FIX.4.0" => "4.0",
"FIX.4.1" => "4.1",
"FIX.4.2" => "4.2",
"FIX.4.3" => "4.3",
"FIX.4.4" => "4.4",
"FIX.5.0" => "5.0",
"FIX.5.0SP1" => "5.0.SP1",
"FIX.5.0SP2" => "5.0.SP2",
"FIXT.1.1" => "FIXT.1.1",
s => s,
};
format!(
"https://www.onixs.biz/fix-dictionary/{}/tagNum_{}.html",
v,
self.1.tag.to_string().as_str()
)
}
pub fn is_num_in_group(&self) -> bool {
fn nth_char_is_uppercase(s: &str, i: usize) -> bool {
s.chars().nth(i).map(|c| c.is_ascii_uppercase()) == Some(true)
}
self.fix_datatype().base_type() == FixDatatype::NumInGroup
|| self.name().ends_with("Len")
|| (self.name().starts_with("No") && nth_char_is_uppercase(self.name(), 2))
}
pub fn fix_datatype(&self) -> FixDatatype {
self.data_type().basetype()
}
pub fn name(&self) -> &str {
self.1.name.as_str()
}
pub fn tag(&self) -> TagU32 {
TagU32::new(self.1.tag).unwrap()
}
pub fn enums(&self) -> Option<impl Iterator<Item = FieldEnum<'_>>> {
self.1
.value_restrictions
.as_ref()
.map(move |v| v.iter().map(move |f| FieldEnum(self.0, f)))
}
pub fn data_type(&self) -> Datatype<'_> {
self.0
.datatype_by_name(self.1.data_type_name.as_str())
.unwrap()
}
pub fn data_tag(&self) -> Option<TagU32> {
self.1
.associated_data_tag
.map(|tag| TagU32::new(tag as u32).unwrap())
}
pub fn required_in_xml_messages(&self) -> bool {
self.1.required
}
pub fn description(&self) -> Option<&str> {
self.1.description.as_deref()
}
}
impl<'a> IsFieldDefinition for Field<'a> {
fn tag(&self) -> TagU32 {
TagU32::new(self.1.tag).expect("Invalid FIX tag (0)")
}
fn name(&self) -> &str {
self.1.name.as_str()
}
fn location(&self) -> FieldLocation {
FieldLocation::Body }
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FieldLocation {
Header,
Body,
Trailer,
}