use std::error::Error;
use std::fmt::Write;
use super::header_constants::ALLOWED_OPT_BLOCK_IDS;
#[derive(Debug, PartialEq, Clone)]
pub struct OptBlock {
id: String,
data: String,
length: usize,
next: Option<Box<OptBlock>>,
}
impl OptBlock {
pub fn new(id: &str, data: &str, next: Option<OptBlock>) -> Result<Self, Box<dyn Error>> {
let mut opt_block = Self::new_empty();
opt_block.set_id(id)?;
opt_block.set_data(data)?;
opt_block.set_next(next);
Ok(opt_block)
}
pub fn new_empty() -> Self {
Self {
id: String::new(),
data: String::new(),
length: 0,
next: None,
}
}
pub fn new_from_str(s: &str, num_opt_blocks: usize) -> Result<Self, Box<dyn Error>> {
if s.len() < 4 {
return Err(
"ERROR TR-31 OPT BLOCK: String too short. Expected at least 4 characters".into(),
);
}
let mut opt_block = Self::new_empty();
opt_block.set_id(&s[..2])?;
let data_start_offset: usize;
if &s[2..4] == "00" {
if s.len() < 256 {
return Err("ERROR TR-31 OPT BLOCK: String containing extended length too short. Expected at least 256 characters".into());
}
let ext_block_len = &s[4..10];
opt_block.length = Self::ext_len_from_str(ext_block_len)?;
data_start_offset = 10;
} else {
opt_block.length = Self::len_from_str(&s[2..4])?;
data_start_offset = 4;
}
if s.len() < opt_block.length {
return Err(format!(
"ERROR TR-31 OPT BLOCK: String too short for given length. Expected at least {} characters.",
opt_block.length
).into());
}
opt_block.set_data(&s[data_start_offset..opt_block.length])?;
if num_opt_blocks > 1 {
let next_block_str = &s[opt_block.length..];
let next_block = OptBlock::new_from_str(next_block_str, num_opt_blocks - 1)?;
opt_block.set_next(Some(next_block));
}
Ok(opt_block)
}
pub fn export_str(&self) -> Result<String, Box<dyn Error>> {
if self.length < 4 {
return Err("ERROR TR-31 OPT BLOCK: Length must be greater than 4, indicating uninitialized OptBlock".into());
}
let mut res = String::new();
res.push_str(&self.id);
if self.length < 256 {
write!(&mut res, "{:02X}", self.length)?;
} else {
write!(&mut res, "0002{:04X}", self.length)?;
}
res.push_str(&self.data);
if let Some(next) = &self.next {
res.push_str(&next.export_str()?);
}
Ok(res)
}
pub fn set_id(&mut self, id: &str) -> Result<(), Box<dyn Error>> {
if Self::is_allowed_id(id) {
self.id = id.to_string();
Ok(())
} else {
Err(format!("ERROR TR-31 OPT BLOCK: Invalid ID: {}", id).into())
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn set_data(&mut self, data: &str) -> Result<(), Box<dyn Error>> {
if self.id.len() != 2 {
return Err("ERROR TR-31 OPT BLOCK: ID not set (has to be set before data)".into());
}
if !data.chars().all(|c| c.is_ascii()) {
return Err(format!(
"ERROR TR-31 OPT BLOCK: Data has non ASCII characters: {}",
data
)
.into());
}
self.data = data.to_string();
self.set_length()?;
Ok(())
}
pub fn data(&self) -> &str {
&self.data
}
fn set_length(&mut self) -> Result<(), Box<dyn Error>> {
let min_len: usize = self.id.len() + 2 + self.data.len();
if min_len < 256 {
self.length = min_len;
} else {
self.length = min_len + 6;
}
if self.length > 65535 {
let old_length = self.length;
self.length = 0;
return Err(format!(
"ERROR TR-31 OPT BLOCK: Block size '{}' is too long (must be max. 65535)",
old_length
)
.into());
}
Ok(())
}
pub fn length(&self) -> &usize {
&self.length
}
pub fn set_next(&mut self, next_block: Option<OptBlock>) {
self.next = next_block.map(Box::new);
}
pub fn next(&self) -> Option<&OptBlock> {
self.next.as_deref()
}
pub fn append(&mut self, opt_block_to_append: OptBlock) {
match &mut self.next {
Some(ref mut next_block) => next_block.append(opt_block_to_append),
None => self.set_next(Some(opt_block_to_append)),
}
}
pub fn is_allowed_id(id: &str) -> bool {
ALLOWED_OPT_BLOCK_IDS.contains(&id)
}
pub fn total_length(&self) -> usize {
let mut total = self.length;
if let Some(next) = &self.next {
total += next.total_length();
}
total
}
fn len_from_str(s: &str) -> Result<usize, Box<dyn Error>> {
if s.len() != 2 {
return Err(Box::<dyn Error>::from(format!(
"ERROR TR-31 OPT BLOCK: Invalid length field: Expected a string with 2 characters, found '{}'",
s
)));
}
let len = usize::from_str_radix(s, 16).map_err(|_| {
Box::<dyn Error>::from(format!("ERROR TR-31 OPT BLOCK: Invalid length field: '{}' is not a valid hexadecimal number", s))
})?;
if len < 4 {
return Err(Box::<dyn Error>::from(format!(
"ERROR TR-31 OPT BLOCK: Invalid length field: value {} is too small (must be at least 4)",
len
)));
}
Ok(len)
}
fn ext_len_from_str(s: &str) -> Result<usize, String> {
if s.len() != 6 {
return Err(format!(
"ERROR TR-31 OPT BLOCK: Invalid extended length field: {}",
s
));
}
if &s[0..2] != "02" {
return Err(format!(
"ERROR TR-31 OPT BLOCK: Invalid length of length field: {}",
&s[0..2]
));
}
let res = usize::from_str_radix(&s[2..6], 16).map_err(|e| e.to_string())?;
if res <= 255 {
return Err(format!(
"ERROR TR-31 OPT BLOCK: Extended length is not greater than 255: {}",
&s[2..6]
));
}
Ok(res)
}
}