use crate::line::{
BlockId, BlockIdParseError, ShortBlockId, ShortBlockIdParsingError, ShortFieldId,
ShortFieldIdParsingError, ShortGroupId, ShortGroupIdParsingError, ShortStrucId,
ShortStrucIdParsingError,
};
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[cfg_attr(
feature = "serde",
derive(serde_with::DeserializeFromStr, serde_with::SerializeDisplay)
)]
pub struct FieldId {
block_id: BlockId,
short_field_id: ShortFieldId,
}
impl FieldId {
#[inline]
pub fn block_id(&self) -> BlockId {
self.block_id
}
#[inline]
pub fn short_field_id(&self) -> ShortFieldId {
self.short_field_id
}
#[inline]
pub const fn new(
short_struc_id: ShortStrucId,
short_group_id: ShortGroupId,
short_block_id: ShortBlockId,
short_field_id: ShortFieldId,
) -> Self {
Self {
block_id: BlockId::new(short_struc_id, short_group_id, short_block_id),
short_field_id,
}
}
}
impl Display for FieldId {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}.{:03}", self.block_id, self.short_field_id))
}
}
impl FromStr for FieldId {
type Err = FieldIdParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (block_s, field_s) = s.rsplit_once('.').ok_or(FieldIdParseError::Format)?;
Ok(FieldId {
block_id: BlockId::from_str(block_s)?,
short_field_id: ShortFieldId::from_str(field_s)?,
})
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum FieldIdParseError {
Format,
Structure,
Group,
Block,
Field,
}
impl Error for FieldIdParseError {}
impl From<ShortStrucIdParsingError> for FieldIdParseError {
#[inline]
fn from(_: ShortStrucIdParsingError) -> Self {
FieldIdParseError::Structure
}
}
impl From<ShortGroupIdParsingError> for FieldIdParseError {
#[inline]
fn from(_: ShortGroupIdParsingError) -> Self {
FieldIdParseError::Group
}
}
impl From<ShortBlockIdParsingError> for FieldIdParseError {
#[inline]
fn from(_: ShortBlockIdParsingError) -> Self {
FieldIdParseError::Block
}
}
impl From<ShortFieldIdParsingError> for FieldIdParseError {
#[inline]
fn from(_: ShortFieldIdParsingError) -> Self {
FieldIdParseError::Field
}
}
impl From<BlockIdParseError> for FieldIdParseError {
#[inline]
fn from(value: BlockIdParseError) -> Self {
match value {
BlockIdParseError::Format => FieldIdParseError::Format,
BlockIdParseError::Structure => FieldIdParseError::Structure,
BlockIdParseError::Group => FieldIdParseError::Group,
BlockIdParseError::Block => FieldIdParseError::Block,
}
}
}
impl Display for FieldIdParseError {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
FieldIdParseError::Format => {
write!(f, "Wrong Field ID format")
}
FieldIdParseError::Structure => {
write!(f, "Cannot parse structure ID")
}
FieldIdParseError::Group => {
write!(f, "Cannot parse group ID")
}
FieldIdParseError::Block => {
write!(f, "Cannot parse block ID")
}
FieldIdParseError::Field => {
write!(f, "Cannot parse field ID")
}
}
}
}
macro_rules! impl_string_cmp {
( $( $t:ty ),* ) => {
$(
impl PartialEq<$t> for FieldId {
#[inline]
fn eq(&self, other: &$t) -> bool {
let s = other.as_ref();
match FieldId::from_str(s) {
Ok(field_id) => *self == field_id,
Err(_) => false,
}
}
}
impl PartialEq<FieldId> for $t {
#[inline]
fn eq(&self, other: &FieldId) -> bool {
other == self
}
}
)*
}
}
impl_string_cmp!(str, &str, String);
#[cfg(test)]
mod tests {
use std::str::FromStr;
use crate::line::id::field_id::FieldId;
use crate::line::{ShortBlockId, ShortFieldId, ShortGroupId, ShortStrucId};
use parameterized::parameterized;
#[parameterized(input = {
"S21",
"S20.001",
"S21.G00.00.0X1",
})]
fn returns_parsing_errors(input: &str) {
assert!(FieldId::from_str(input).is_err());
}
#[test]
fn test_string_equals() {
let id = FieldId::new(
ShortStrucId::from_u8_lossy(21),
ShortGroupId::from_u8_lossy(0),
ShortBlockId::from_u8_lossy(13),
ShortFieldId::from_u16_lossy(123),
);
assert_eq!(id, "S21.G00.13.123");
assert_eq!(id, "S21.G00.13.123".to_string());
assert_eq!("S21.G00.13.123", id);
assert_eq!("S21.G00.13.123".to_string(), id);
}
#[parameterized(input = {
"S21.G00.13.124",
"S21.G00.14.123",
"S22.G00.13.123",
"S21.G01.13.123",
"S21.G00.13",
"abc",
"",
"S21.G00.13.123 ",
" S21.G00.13.123"
})]
fn test_string_not_equals(input: &str) {
let id = FieldId::new(
ShortStrucId::from_u8_lossy(21),
ShortGroupId::from_u8_lossy(0),
ShortBlockId::from_u8_lossy(13),
ShortFieldId::from_u16_lossy(123),
);
assert_ne!(id, input);
assert_ne!(input, id);
assert_ne!(id, input.to_string());
assert_ne!(input.to_string(), id);
}
}