#[error("Not an SPSS system file")]
NotASystemFile,
+ #[error("Invalid magic number {0:?}")]
+ BadMagic([u8; 4]),
+
#[error("I/O error ({source})")]
Io {
#[from]
zheader: Option<ZHeader>,
}
-/// Magic number for a regular system file.
-pub const ASCII_MAGIC: &[u8; 4] = b"$FL2";
-
-/// Magic number for a system file that contains zlib-compressed data.
-pub const ASCII_ZMAGIC: &[u8; 4] = b"$FL3";
-
-/// Magic number for an EBDIC-encoded system file. This is `$FL2` encoded in
-/// EBCDIC.
-pub const EBCDIC_MAGIC: &[u8; 4] = &[0x5b, 0xc6, 0xd3, 0xf2];
-
pub struct FileHeader {
- /// First 4 bytes of the file, one of `ASCII_MAGIC`, `ASCII_ZMAGIC`, and
- /// `EBCDIC_MAGIC`.
- pub magic: [u8; 4],
-
- /// True if `magic` indicates that this file contained zlib-compressed data.
- pub is_zsav: bool,
-
- /// True if `magic` indicates that this file contained EBCDIC data.
- pub is_ebcdic: bool,
+ /// Magic number.
+ pub magic: Magic,
/// Endianness of the data in the file header.
pub endianness: Endian,
pub file_label: [u8; 64],
}
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct Magic([u8; 4]);
+
+impl Magic {
+ /// Magic number for a regular system file.
+ pub const SAV: Magic = Magic(*b"$FL2");
+
+ /// Magic number for a system file that contains zlib-compressed data.
+ pub const ZSAV: Magic = Magic(*b"$FL3");
+
+ /// Magic number for an EBDIC-encoded system file. This is `$FL2` encoded
+ /// in EBCDIC.
+ pub const EBCDIC: Magic = Magic([0x5b, 0xc6, 0xd3, 0xf2]);
+}
+
+impl TryFrom<[u8; 4]> for Magic {
+ type Error = Error;
+
+ fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
+ let magic = Magic(value);
+ match magic {
+ Magic::SAV | Magic::ZSAV | Magic::EBCDIC => Ok(magic),
+ _ => Err(Error::BadMagic(value)),
+ }
+ }
+}
+
impl<R: Read + Seek> Reader<R> {
pub fn new(r: R, warn: impl Fn(Warning)) -> Result<Reader<R>, Error> {
let mut r = BufReader::new(r);
}
}
let _: [u8; 4] = read_bytes(&mut r)?;
- let zheader = match header.is_zsav {
- true => Some(read_zheader(&mut r, e)?),
- false => None,
+ let zheader = match header.magic {
+ Magic::ZSAV => Some(read_zheader(&mut r, e)?),
+ _ => None,
};
Ok(Reader {
fn read_header<R: Read>(r: &mut R, warn: impl Fn(Warning)) -> Result<FileHeader, Error> {
let magic: [u8; 4] = read_bytes(r)?;
- let (is_zsav, is_ebcdic) = match &magic {
- ASCII_MAGIC => (false, false),
- ASCII_ZMAGIC => (true, false),
- EBCDIC_MAGIC => (false, true),
- _ => return Err(Error::NotASystemFile),
- };
+ let magic: Magic = magic.try_into().map_err(|_| Error::NotASystemFile)?;
let eye_catcher: [u8; 60] = read_bytes(r)?;
let layout_code: [u8; 4] = read_bytes(r)?;
(nominal_case_size <= i32::MAX as u32 / 16).then_some(nominal_case_size);
let compression_code: u32 = endianness.parse(read_bytes(r)?);
- let compression = match (is_zsav, compression_code) {
- (false, 0) => None,
- (false, 1) => Some(Compression::Simple),
- (true, 2) => Some(Compression::ZLib),
- (false, code) => return Err(Error::InvalidSavCompression(code)),
- (true, code) => return Err(Error::InvalidZsavCompression(code)),
+ let compression = match (magic, compression_code) {
+ (Magic::ZSAV, 2) => Some(Compression::ZLib),
+ (Magic::ZSAV, code) => return Err(Error::InvalidZsavCompression(code)),
+ (_, 0) => None,
+ (_, 1) => Some(Compression::Simple),
+ (_, code) => return Err(Error::InvalidSavCompression(code)),
};
let weight_index: u32 = endianness.parse(read_bytes(r)?);
Ok(FileHeader {
magic,
- is_zsav,
- is_ebcdic,
endianness,
weight_index,
nominal_case_size,