use crate::line::id::short_block_id::{ShortBlockId, ShortBlockIdParsingError};
use crate::line::id::short_group_id::{ShortGroupId, ShortGroupIdParsingError};
use crate::line::id::short_struc_id::{ShortStrucId, ShortStrucIdParsingError};
use crate::line::{FieldId, ShortFieldId};
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 BlockId {
short_struc_id: ShortStrucId,
short_group_id: ShortGroupId,
short_block_id: ShortBlockId,
}
impl BlockId {
#[inline]
pub const fn new(
short_struc_id: ShortStrucId,
short_group_id: ShortGroupId,
short_block_id: ShortBlockId,
) -> BlockId {
BlockId {
short_struc_id,
short_group_id,
short_block_id,
}
}
#[inline]
pub fn short_struc_id(&self) -> ShortStrucId {
self.short_struc_id
}
#[inline]
pub fn short_group_id(&self) -> ShortGroupId {
self.short_group_id
}
#[inline]
pub fn short_block_id(&self) -> ShortBlockId {
self.short_block_id
}
#[inline]
pub fn with_field_id(&self, field_id: ShortFieldId) -> FieldId {
FieldId::new(
self.short_struc_id,
self.short_group_id,
self.short_block_id,
field_id,
)
}
}
impl Display for BlockId {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{}.{}.{}",
self.short_struc_id, self.short_group_id, self.short_block_id
))
}
}
impl FromStr for BlockId {
type Err = BlockIdParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut split_n = s.splitn(3, '.');
let next_split = split_n.next().ok_or(BlockIdParseError::Format)?;
let short_struc_id = ShortStrucId::from_str(next_split)?;
let next_split = split_n.next().ok_or(BlockIdParseError::Format)?;
let short_group_id = ShortGroupId::from_str(next_split)?;
let next_split = split_n.next().ok_or(BlockIdParseError::Format)?;
let short_block_id = ShortBlockId::from_str(next_split)?;
Ok(BlockId {
short_struc_id,
short_group_id,
short_block_id,
})
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum BlockIdParseError {
Format,
Structure,
Group,
Block,
}
impl Error for BlockIdParseError {}
impl From<ShortStrucIdParsingError> for BlockIdParseError {
#[inline]
fn from(_: ShortStrucIdParsingError) -> Self {
BlockIdParseError::Structure
}
}
impl From<ShortGroupIdParsingError> for BlockIdParseError {
#[inline]
fn from(_: ShortGroupIdParsingError) -> Self {
BlockIdParseError::Group
}
}
impl From<ShortBlockIdParsingError> for BlockIdParseError {
#[inline]
fn from(_: ShortBlockIdParsingError) -> Self {
BlockIdParseError::Block
}
}
impl Display for BlockIdParseError {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
BlockIdParseError::Format => {
write!(f, "Wrong Field ID format")
}
BlockIdParseError::Structure => {
write!(f, "Cannot parse structure ID")
}
BlockIdParseError::Group => {
write!(f, "Cannot parse group ID")
}
BlockIdParseError::Block => {
write!(f, "Cannot parse block ID")
}
}
}
}
macro_rules! impl_string_cmp {
( $( $t:ty ),* ) => {
$(
impl PartialEq<$t> for BlockId {
#[inline]
fn eq(&self, other: &$t) -> bool {
let s = other.as_ref();
match BlockId::from_str(s) {
Ok(block_id) => *self == block_id,
Err(_) => false,
}
}
}
impl PartialEq<BlockId> for $t {
#[inline]
fn eq(&self, other: &BlockId) -> bool {
other == self
}
}
)*
}
}
impl_string_cmp!(str, &str, String);
#[cfg(test)]
mod tests {
use crate::line::{BlockId, ShortBlockId, ShortGroupId, ShortStrucId};
use parameterized::parameterized;
use std::str::FromStr;
#[parameterized(input = {
"S215G00.00",
"S21.G00|00",
"S2X.G00.00",
"S21.G0X.00",
"S21.G00.0X",
"S21.G00.00.",
"S21.G00.00.001"
})]
fn returns_parsing_errors(input: &str) {
assert!(BlockId::from_str(input).is_err());
}
#[test]
fn can_parse_string() {
assert_eq!(
BlockId::from_str("S21.G00.00"),
Ok(BlockId {
short_struc_id: ShortStrucId::from_u8_lossy(21),
short_group_id: ShortGroupId::from_u8_lossy(0),
short_block_id: ShortBlockId::from_u8_lossy(0),
})
);
}
#[test]
fn test_string_equals() {
let id = BlockId::new(
ShortStrucId::from_u8_lossy(21),
ShortGroupId::from_u8_lossy(0),
ShortBlockId::from_u8_lossy(13),
);
assert_eq!(id, "S21.G00.13");
assert_eq!(id, "S21.G00.13".to_string());
assert_eq!("S21.G00.13", id);
assert_eq!("S21.G00.13".to_string(), id);
}
#[parameterized(input = {
"S21.G00.14",
"S22.G00.13",
"S21.G01.13",
"S21.G00.13.001",
"abc",
"",
"S21.G00.13 ",
" S21.G00.13"
})]
fn test_string_not_equals(input: &str) {
let id = BlockId::new(
ShortStrucId::from_u8_lossy(21),
ShortGroupId::from_u8_lossy(0),
ShortBlockId::from_u8_lossy(13),
);
assert_ne!(id, input);
assert_ne!(input, id);
assert_ne!(id, input.to_string());
assert_ne!(input.to_string(), id);
}
}