use std::io::{Read, Write};
use parsenic::{Read as _, Reader};
use super::Chunk;
use crate::{
consts, decode::Error as DecoderError, decoder::Parser,
encode::Error as EncoderError, encoder::Enc, parsing::Read as _, zlib,
};
#[derive(Clone, Debug)]
pub struct InternationalText {
pub key: String,
pub langtag: String,
pub transkey: String,
pub val: String,
pub compressed: bool,
}
impl InternationalText {
pub(crate) fn parse<R: Read>(
parse: &mut Parser<R>,
) -> Result<Chunk, DecoderError> {
let buffer = parse.raw()?;
let mut reader = Reader::new(&buffer);
let key = {
let key = reader.strz()?;
let key_len = key.len();
(1..=79)
.contains(&key_len)
.then_some(key)
.ok_or(DecoderError::KeySize(key_len))?
};
let compressed = match reader.u8()? {
0 => false,
1 => true,
_ => return Err(DecoderError::CompressionMethod),
};
let _compression_method = {
let compression_method = reader.u8()?;
(compression_method == 0)
.then_some(compression_method)
.ok_or(DecoderError::CompressionMethod)?
};
let langtag = reader.strz()?;
let transkey = reader.strz()?;
let data = reader
.slice(
parse.len() - (key.len() + langtag.len() + transkey.len() + 5),
)?
.to_vec();
let val = if compressed {
String::from_utf8_lossy(&zlib::decompress(&data)?).to_string()
} else {
String::from_utf8_lossy(&data).to_string()
};
reader.end().unwrap();
Ok(Chunk::InternationalText(InternationalText {
key,
langtag,
transkey,
val,
compressed,
}))
}
pub(crate) fn write<W: Write>(
&self,
enc: &mut Enc<W>,
) -> Result<(), EncoderError> {
let k_len = self.key.len();
if !(1..=79).contains(&k_len) {
return Err(EncoderError::KeySize(k_len));
}
let zdata = if self.compressed {
let mut data = Vec::new();
zlib::compress(&mut data, self.val.as_bytes(), enc.level());
Some(data)
} else {
None
};
let len = if let Some(ref zdata) = zdata {
zdata.len()
} else {
self.val.len()
};
enc.prepare(
self.key.len() + self.langtag.len() + self.transkey.len() + len + 5,
consts::ITEXT,
)?;
enc.str(&self.key)?;
enc.u8(self.compressed as u8)?;
enc.u8(0)?;
enc.str(&self.langtag)?;
enc.str(&self.transkey)?;
if let Some(zdata) = zdata {
enc.raw(&zdata)?;
} else {
enc.raw(self.val.as_bytes())?;
}
enc.write_crc()
}
}