use binrw::helpers::count;
use binrw::io::{Read, Seek, Write};
use binrw::{BinRead, BinResult, BinWrite, Endian, NamedArgs};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ZipString {
UTF8(String),
Raw(Vec<u8>),
}
impl ZipString {
pub fn from_bytes(raw: Vec<u8>, declared_utf8: bool) -> Self {
if !declared_utf8 && !raw.is_ascii() {
return Self::Raw(raw);
}
match String::from_utf8(raw) {
Ok(string) => Self::UTF8(string),
Err(error) => Self::Raw(error.into_bytes()),
}
}
pub fn as_bytes(&self) -> &[u8] {
match self {
Self::UTF8(string) => string.as_bytes(),
Self::Raw(raw) => raw,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Self::UTF8(string) => Some(string),
Self::Raw(_) => None,
}
}
}
impl PartialEq<[u8]> for ZipString {
fn eq(&self, other: &[u8]) -> bool {
self.as_bytes() == other
}
}
impl PartialEq<&[u8]> for ZipString {
fn eq(&self, other: &&[u8]) -> bool {
self.as_bytes() == *other
}
}
impl PartialEq<str> for ZipString {
fn eq(&self, other: &str) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl From<String> for ZipString {
fn from(value: String) -> Self {
Self::UTF8(value)
}
}
impl From<&str> for ZipString {
fn from(value: &str) -> Self {
Self::UTF8(value.to_owned())
}
}
#[derive(Clone, NamedArgs)]
pub struct ZipStringArgs {
pub count: usize,
#[named_args(default = false)]
pub utf8: bool,
}
impl BinRead for ZipString {
type Args<'a> = ZipStringArgs;
fn read_options<R: Read + Seek>(reader: &mut R, endian: Endian, args: Self::Args<'_>) -> BinResult<Self> {
let raw: Vec<u8> = count(args.count)(reader, endian, ())?;
Ok(Self::from_bytes(raw, args.utf8))
}
}
impl BinWrite for ZipString {
type Args<'a> = ();
fn write_options<W: Write + Seek>(&self, writer: &mut W, endian: Endian, _: ()) -> BinResult<()> {
self.as_bytes().write_options(writer, endian, ())
}
}