From f8a580d1832c18827ba82e634f4e1b4e76b762d5 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 28 Jan 2024 16:41:58 -0800 Subject: [PATCH] separate warnings and errors --- rust/src/dictionary.rs | 2 +- rust/src/main.rs | 32 +++-- rust/src/raw.rs | 279 +++++++++++++++++++++-------------------- 3 files changed, 169 insertions(+), 144 deletions(-) diff --git a/rust/src/dictionary.rs b/rust/src/dictionary.rs index 6662aaa94e..ace97b03f7 100644 --- a/rust/src/dictionary.rs +++ b/rust/src/dictionary.rs @@ -36,7 +36,7 @@ impl PartialOrd for VarWidth { impl VarWidth { pub const MAX_STRING: u16 = 32767; - fn n_dict_indexes(self) -> usize { + pub fn n_dict_indexes(self) -> usize { match self { VarWidth::Numeric => 1, VarWidth::String(w) => div_ceil(w as usize, 8), diff --git a/rust/src/main.rs b/rust/src/main.rs index bf6abe9b8f..dece725c65 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -17,7 +17,7 @@ use anyhow::Result; use clap::{Parser, ValueEnum}; use encoding_rs::Encoding; -use pspp::raw::{Reader, Record, Magic}; +use pspp::raw::{Magic, Reader, Record}; use std::fs::File; use std::io::BufReader; use std::path::{Path, PathBuf}; @@ -78,20 +78,27 @@ fn main() -> Result<()> { Ok(()) } -fn dissect(file_name: &Path, max_cases: u64, mode: Mode, encoding: Option<&'static Encoding>) -> Result<()> { +fn dissect( + file_name: &Path, + max_cases: u64, + mode: Mode, + encoding: Option<&'static Encoding>, +) -> Result<()> { let reader = File::open(file_name)?; let reader = BufReader::new(reader); let mut reader = Reader::new(reader, |warning| println!("{warning}"))?; match mode { Mode::Identify => { - let Record::Header(header) = reader.next().unwrap()? else { unreachable!() }; + let Record::Header(header) = reader.next().unwrap()? else { + unreachable!() + }; match header.magic { Magic::Sav => println!("SPSS System File"), Magic::Zsav => println!("SPSS System File with Zlib compression"), Magic::Ebcdic => println!("EBCDIC-encoded SPSS System File"), } - return Ok(()) + return Ok(()); } Mode::Raw => { for header in reader { @@ -108,15 +115,20 @@ fn dissect(file_name: &Path, max_cases: u64, mode: Mode, encoding: Option<&'stat } } } - Mode::Cooked => { /* + Mode::Decoded => { let headers: Vec = reader.collect::, _>>()?; - let encoding = encoding_from_headers(&headers, &|e| eprintln!("{e}"))?; - let (headers, _) = decode(headers, encoding, &|e| eprintln!("{e}"))?; - for header in headers { - println!("{header:?}"); } - */ +*/ + Mode::Cooked => { + /* + let headers: Vec = reader.collect::, _>>()?; + let encoding = encoding_from_headers(&headers, &|e| eprintln!("{e}"))?; + let (headers, _) = decode(headers, encoding, &|e| eprintln!("{e}"))?; + for header in headers { + println!("{header:?}"); + } + */ } } diff --git a/rust/src/raw.rs b/rust/src/raw.rs index e75e5a05e8..a38422950a 100644 --- a/rust/src/raw.rs +++ b/rust/src/raw.rs @@ -39,9 +39,6 @@ pub enum Error { #[error("Invalid ZSAV compression code {0}")] InvalidZsavCompression(u32), - #[error("Variable record at offset {offset:#x} specifies width {width} not in valid range [-1,255).")] - BadVariableWidth { offset: u64, width: i32 }, - #[error("Document record at offset {offset:#x} has document line count ({n}) greater than the maximum number {max}.")] BadDocumentLength { offset: u64, n: usize, max: usize }, @@ -72,23 +69,6 @@ pub enum Error { #[error("At offset {offset:#x}, number of variables indexes for value labels ({n}) is greater than the maximum number ({max}).")] TooManyVarIndexes { offset: u64, n: u32, max: u32 }, - #[error("At offset {offset:#x}, at least one valid variable index for value labels is required but none were specified.")] - NoVarIndexes { offset: u64 }, - - #[error("At offset {offset:#x}, the first variable index is for a {var_type} variable but the following variable indexes are for {} variables: {wrong_types:?}", var_type.opposite())] - MixedVarTypes { - offset: u64, - var_type: VarType, - wrong_types: Vec, - }, - - #[error("At offset {offset:#x}, one or more variable indexes for value labels were not in the valid range [1,{max}]: {invalid:?}")] - InvalidVarIndexes { - offset: u64, - max: usize, - invalid: Vec, - }, - #[error("At offset {offset:#x}, record type 7 subtype {subtype} is too large with element size {size} and {count} elements.")] ExtensionRecordTooLarge { offset: u64, @@ -125,6 +105,29 @@ pub enum Error { expected_n_blocks: u64, ztrailer_len: u64, }, +} + +#[derive(ThisError, Debug)] +pub enum Warning { + #[error("Unexpected end of data inside extension record.")] + UnexpectedEndOfData, + + #[error("At offset {offset:#x}, at least one valid variable index for value labels is required but none were specified.")] + NoVarIndexes { offset: u64 }, + + #[error("At offset {offset:#x}, the first variable index is for a {var_type} variable but the following variable indexes are for {} variables: {wrong_types:?}", var_type.opposite())] + MixedVarTypes { + offset: u64, + var_type: VarType, + wrong_types: Vec, + }, + + #[error("At offset {offset:#x}, one or more variable indexes for value labels were not in the valid range [1,{max}]: {invalid:?}")] + InvalidVarIndexes { + offset: u64, + max: usize, + invalid: Vec, + }, #[error("At offset {offset:#x}, {record} has bad size {size} bytes instead of the expected {expected_size}.")] BadRecordSize { @@ -196,6 +199,12 @@ pub enum Error { TBD, } +impl From for Warning { + fn from(_source: IoError) -> Self { + Self::UnexpectedEndOfData + } +} + #[derive(Clone, Debug)] pub enum Record { Header(HeaderRecord), @@ -248,7 +257,7 @@ impl Record { reader: &mut R, endian: Endian, var_types: &[VarType], - warn: &Box, + warn: &Box, ) -> Result, Error> where R: Read + Seek, @@ -269,7 +278,7 @@ impl Record { } } - fn decode<'a>(&'a self, decoder: &Decoder) -> Result, Error> { + pub fn decode<'a>(&'a self, decoder: &Decoder) -> Result, Error> { Ok(match self { Record::Header(record) => record.decode(decoder), Record::Variable(record) => record.decode(decoder), @@ -280,14 +289,14 @@ impl Record { Record::VarDisplay(record) => DecodedRecord::VarDisplay(record.clone()), Record::MultipleResponse(record) => record.decode(decoder), Record::LongStringValueLabels(record) => { - DecodedRecord::LongStringValueLabels(record.decode(decoder)?) + DecodedRecord::LongStringValueLabels(record.decode(decoder)) } Record::LongStringMissingValues(record) => { DecodedRecord::LongStringMissingValues(record.decode(decoder)) } Record::Encoding(record) => DecodedRecord::Encoding(record.clone()), Record::NumberOfCases(record) => DecodedRecord::NumberOfCases(record.clone()), - Record::Text(record) => record.decode(decoder)?, + Record::Text(record) => record.decode(decoder), Record::OtherExtension(record) => DecodedRecord::OtherExtension(record.clone()), Record::EndOfHeaders(record) => DecodedRecord::EndOfHeaders(record.clone()), Record::ZHeader(record) => DecodedRecord::ZHeader(record.clone()), @@ -476,17 +485,17 @@ impl HeaderRecord { pub struct Decoder { pub encoding: &'static Encoding, - pub warn: Box, + pub warn: Box, } impl Decoder { - fn warn(&self, error: Error) { - (self.warn)(error) + fn warn(&self, warning: Warning) { + (self.warn)(warning) } fn decode_slice<'a>(&self, input: &'a [u8]) -> Cow<'a, str> { let (output, malformed) = self.encoding.decode_without_bom_handling(input); if malformed { - self.warn(Error::MalformedString { + self.warn(Warning::MalformedString { encoding: self.encoding.name().into(), text: output.clone().into(), }); @@ -839,7 +848,7 @@ where R: Read + Seek + 'static, { reader: Option, - warn: Box, + warn: Box, header: HeaderRecord, var_types: Vec, @@ -853,7 +862,7 @@ where { pub fn new(mut reader: R, warn: F) -> Result where - F: Fn(Error) + 'static, + F: Fn(Warning) + 'static, { let header = HeaderRecord::read(&mut reader)?; Ok(Self { @@ -1431,7 +1440,7 @@ impl ValueLabelRecord, RawString> { r: &mut R, endian: Endian, var_types: &[VarType], - warn: &Box, + warn: &Box, ) -> Result, Error> { let label_offset = r.stream_position()?; let n: u32 = endian.parse(read_bytes(r)?); @@ -1485,7 +1494,7 @@ impl ValueLabelRecord, RawString> { } } if !invalid_indexes.is_empty() { - warn(Error::InvalidVarIndexes { + warn(Warning::InvalidVarIndexes { offset: index_offset, max: var_types.len(), invalid: invalid_indexes, @@ -1493,7 +1502,7 @@ impl ValueLabelRecord, RawString> { } let Some(&first_index) = dict_indexes.first() else { - warn(Error::NoVarIndexes { + warn(Warning::NoVarIndexes { offset: index_offset, }); return Ok(None); @@ -1509,7 +1518,7 @@ impl ValueLabelRecord, RawString> { } }); if !wrong_type_indexes.is_empty() { - warn(Error::MixedVarTypes { + warn(Warning::MixedVarTypes { offset: index_offset, var_type, wrong_types: wrong_type_indexes, @@ -1622,7 +1631,7 @@ trait ExtensionRecord { const SIZE: Option; const COUNT: Option; const NAME: &'static str; - fn parse(ext: &Extension, endian: Endian) -> Result; + fn parse(ext: &Extension, endian: Endian) -> Result; } #[derive(Clone, Debug)] @@ -1642,7 +1651,7 @@ impl ExtensionRecord for IntegerInfoRecord { const COUNT: Option = Some(8); const NAME: &'static str = "integer record"; - fn parse(ext: &Extension, endian: Endian) -> Result { + fn parse(ext: &Extension, endian: Endian) -> Result { ext.check_size::()?; let mut input = &ext.data[..]; @@ -1674,7 +1683,7 @@ impl ExtensionRecord for FloatInfoRecord { const COUNT: Option = Some(3); const NAME: &'static str = "floating point record"; - fn parse(ext: &Extension, endian: Endian) -> Result { + fn parse(ext: &Extension, endian: Endian) -> Result { ext.check_size::()?; let mut input = &ext.data[..]; @@ -1705,7 +1714,7 @@ pub enum MultipleResponseType { } impl MultipleResponseType { - fn parse(input: &[u8]) -> Result<(MultipleResponseType, &[u8]), Error> { + fn parse(input: &[u8]) -> Result<(MultipleResponseType, &[u8]), Warning> { let (mr_type, input) = match input.split_first() { Some((b'C', input)) => (MultipleResponseType::MultipleCategory, input), Some((b'D', input)) => { @@ -1724,7 +1733,7 @@ impl MultipleResponseType { } else if let Some(rest) = input.strip_prefix(b" 11 ") { (CategoryLabels::VarLabels, rest) } else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let (value, input) = parse_counted_string(input)?; ( @@ -1732,7 +1741,7 @@ impl MultipleResponseType { input, ) } - _ => return Err(Error::TBD), + _ => return Err(Warning::TBD), }; Ok((mr_type, input)) } @@ -1751,14 +1760,14 @@ where } impl MultipleResponseSet { - fn parse(input: &[u8]) -> Result<(Self, &[u8]), Error> { + fn parse(input: &[u8]) -> Result<(Self, &[u8]), Warning> { let Some(equals) = input.iter().position(|&b| b == b'=') else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let (name, input) = input.split_at(equals); let (mr_type, input) = MultipleResponseType::parse(input)?; let Some(input) = input.strip_prefix(b" ") else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let (label, mut input) = parse_counted_string(input)?; let mut vars = Vec::new(); @@ -1766,7 +1775,7 @@ impl MultipleResponseSet { match input.split_first() { Some((b' ', rest)) => { let Some(length) = rest.iter().position(|b| b" \n".contains(b)) else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let (var, rest) = rest.split_at(length); if !var.is_empty() { @@ -1774,7 +1783,7 @@ impl MultipleResponseSet { } input = rest; } - _ => return Err(Error::TBD), + _ => return Err(Warning::TBD), } } while input.first() == Some(&b'\n') { @@ -1794,13 +1803,13 @@ impl MultipleResponseSet { fn decode<'a>( &'a self, decoder: &Decoder, - ) -> Result>, Error> { + ) -> Result>, Warning> { let mut short_names = Vec::with_capacity(self.short_names.len()); for short_name in self.short_names.iter() { if let Some(short_name) = decoder .decode_identifier(short_name) - .map_err(|err| Error::InvalidMrSetName(err)) - .warn_on_error(&decoder.warn) + .map_err(|err| Warning::InvalidMrSetName(err)) + .issue_warning(&decoder.warn) { short_names.push(short_name); } @@ -1808,7 +1817,7 @@ impl MultipleResponseSet { Ok(MultipleResponseSet { name: decoder .decode_identifier(&self.name) - .map_err(|err| Error::InvalidMrSetVariableName(err))?, + .map_err(|err| Warning::InvalidMrSetVariableName(err))?, label: decoder.decode(&self.label), mr_type: self.mr_type.clone(), short_names: short_names, @@ -1828,7 +1837,7 @@ impl ExtensionRecord for MultipleResponseRecord { const COUNT: Option = None; const NAME: &'static str = "multiple response set record"; - fn parse(ext: &Extension, _endian: Endian) -> Result { + fn parse(ext: &Extension, _endian: Endian) -> Result { ext.check_size::()?; let mut input = &ext.data[..]; @@ -1846,7 +1855,7 @@ impl MultipleResponseRecord { fn decode<'a>(&'a self, decoder: &Decoder) -> DecodedRecord { let mut sets = Vec::new(); for set in self.0.iter() { - if let Some(set) = set.decode(decoder).warn_on_error(&decoder.warn) { + if let Some(set) = set.decode(decoder).issue_warning(&decoder.warn) { sets.push(set); } } @@ -1854,20 +1863,20 @@ impl MultipleResponseRecord { } } -fn parse_counted_string(input: &[u8]) -> Result<(RawString, &[u8]), Error> { +fn parse_counted_string(input: &[u8]) -> Result<(RawString, &[u8]), Warning> { let Some(space) = input.iter().position(|&b| b == b' ') else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let Ok(length) = from_utf8(&input[..space]) else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let Ok(length): Result = length.parse() else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let input = &input[space + 1..]; if input.len() < length { - return Err(Error::TBD); + return Err(Warning::TBD); }; let (string, rest) = input.split_at(length); @@ -1889,13 +1898,13 @@ impl Measure { } } - fn try_decode(source: u32) -> Result, Error> { + fn try_decode(source: u32) -> Result, Warning> { match source { 0 => Ok(None), 1 => Ok(Some(Measure::Nominal)), 2 => Ok(Some(Measure::Ordinal)), 3 => Ok(Some(Measure::Scale)), - _ => Err(Error::InvalidMeasurement(source)), + _ => Err(Warning::InvalidMeasurement(source)), } } } @@ -1908,13 +1917,13 @@ pub enum Alignment { } impl Alignment { - fn try_decode(source: u32) -> Result, Error> { + fn try_decode(source: u32) -> Result, Warning> { match source { 0 => Ok(None), 1 => Ok(Some(Alignment::Left)), 2 => Ok(Some(Alignment::Right)), 3 => Ok(Some(Alignment::Center)), - _ => Err(Error::InvalidAlignment(source)), + _ => Err(Warning::InvalidAlignment(source)), } } @@ -1943,10 +1952,10 @@ impl VarDisplayRecord { ext: &Extension, n_vars: usize, endian: Endian, - warn: &Box, - ) -> Result { + warn: &Box, + ) -> Result { if ext.size != 4 { - return Err(Error::BadRecordSize { + return Err(Warning::BadRecordSize { offset: ext.offsets.start, record: String::from("variable display record"), size: ext.size, @@ -1959,18 +1968,18 @@ impl VarDisplayRecord { } else if ext.count as usize == 2 * n_vars { false } else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let mut var_displays = Vec::new(); let mut input = &ext.data[..]; for _ in 0..n_vars { let measure = Measure::try_decode(endian.parse(read_bytes(&mut input).unwrap())) - .warn_on_error(&warn) + .issue_warning(&warn) .flatten(); let width = has_width.then(|| endian.parse(read_bytes(&mut input).unwrap())); let alignment = Alignment::try_decode(endian.parse(read_bytes(&mut input).unwrap())) - .warn_on_error(&warn) + .issue_warning(&warn) .flatten(); var_displays.push(VarDisplay { measure, @@ -2019,7 +2028,7 @@ impl ExtensionRecord for LongStringMissingValueRecord> { const COUNT: Option = None; const NAME: &'static str = "long string missing values record"; - fn parse(ext: &Extension, endian: Endian) -> Result { + fn parse(ext: &Extension, endian: Endian) -> Result { ext.check_size::()?; let mut input = &ext.data[..]; @@ -2030,7 +2039,7 @@ impl ExtensionRecord for LongStringMissingValueRecord> { let value_len: u32 = endian.parse(read_bytes(&mut input)?); if value_len != 8 { let offset = (ext.data.len() - input.len() - 8) as u64 + ext.offsets.start; - return Err(Error::BadLongMissingValueLength { + return Err(Warning::BadLongMissingValueLength { record_offset: ext.offsets.start, offset, value_len, @@ -2074,8 +2083,8 @@ impl LongStringMissingValueRecord> { for mv in self.0.iter() { if let Some(mv) = mv .decode(decoder) - .map_err(|err| Error::InvalidLongStringMissingValueVariableName(err)) - .warn_on_error(&decoder.warn) + .map_err(|err| Warning::InvalidLongStringMissingValueVariableName(err)) + .issue_warning(&decoder.warn) { mvs.push(mv); } @@ -2093,11 +2102,11 @@ impl ExtensionRecord for EncodingRecord { const COUNT: Option = None; const NAME: &'static str = "encoding record"; - fn parse(ext: &Extension, _endian: Endian) -> Result { + fn parse(ext: &Extension, _endian: Endian) -> Result { ext.check_size::()?; Ok(Record::Encoding(EncodingRecord( - String::from_utf8(ext.data.clone()).map_err(|_| Error::BadEncodingName { + String::from_utf8(ext.data.clone()).map_err(|_| Warning::BadEncodingName { offset: ext.offsets.start, })?, ))) @@ -2119,7 +2128,7 @@ impl ExtensionRecord for NumberOfCasesRecord { const COUNT: Option = Some(2); const NAME: &'static str = "extended number of cases record"; - fn parse(ext: &Extension, endian: Endian) -> Result { + fn parse(ext: &Extension, endian: Endian) -> Result { ext.check_size::()?; let mut input = &ext.data[..]; @@ -2159,26 +2168,26 @@ impl TextRecord { text: extension.data.into(), } } - pub fn decode<'a>(&self, decoder: &Decoder) -> Result { + pub fn decode<'a>(&self, decoder: &Decoder) -> DecodedRecord { match self.rec_type { - TextRecordType::VariableSets => Ok(DecodedRecord::VariableSets( - VariableSetRecord::decode(self, decoder), - )), - TextRecordType::ProductInfo => Ok(DecodedRecord::ProductInfo( - ProductInfoRecord::decode(self, decoder), - )), - TextRecordType::LongNames => Ok(DecodedRecord::LongNames(LongNamesRecord::decode( - self, decoder, - ))), - TextRecordType::VeryLongStrings => Ok(DecodedRecord::VeryLongStrings( - VeryLongStringsRecord::decode(self, decoder), - )), - TextRecordType::FileAttributes => Ok(DecodedRecord::FileAttributes( - FileAttributeRecord::decode(self, decoder), - )), - TextRecordType::VariableAttributes => Ok(DecodedRecord::VariableAttributes( - VariableAttributeRecord::decode(self, decoder), - )), + TextRecordType::VariableSets => { + DecodedRecord::VariableSets(VariableSetRecord::decode(self, decoder)) + } + TextRecordType::ProductInfo => { + DecodedRecord::ProductInfo(ProductInfoRecord::decode(self, decoder)) + } + TextRecordType::LongNames => { + DecodedRecord::LongNames(LongNamesRecord::decode(self, decoder)) + } + TextRecordType::VeryLongStrings => { + DecodedRecord::VeryLongStrings(VeryLongStringsRecord::decode(self, decoder)) + } + TextRecordType::FileAttributes => { + DecodedRecord::FileAttributes(FileAttributeRecord::decode(self, decoder)) + } + TextRecordType::VariableAttributes => { + DecodedRecord::VariableAttributes(VariableAttributeRecord::decode(self, decoder)) + } } } } @@ -2190,14 +2199,14 @@ pub struct VeryLongString { } impl VeryLongString { - fn parse(decoder: &Decoder, input: &str) -> Result { + fn parse(decoder: &Decoder, input: &str) -> Result { let Some((short_name, length)) = input.split_once('=') else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let short_name = decoder .new_identifier(short_name) - .map_err(Error::InvalidLongStringName)?; - let length = length.parse().map_err(|_| Error::TBD)?; + .map_err(Warning::InvalidLongStringName)?; + let length = length.parse().map_err(|_| Warning::TBD)?; Ok(VeryLongString { short_name, length }) } } @@ -2214,7 +2223,7 @@ impl VeryLongStringsRecord { .map(|s| s.trim_end_matches('\t')) .filter(|s| !s.is_empty()) { - if let Some(vls) = VeryLongString::parse(decoder, tuple).warn_on_error(&decoder.warn) { + if let Some(vls) = VeryLongString::parse(decoder, tuple).issue_warning(&decoder.warn) { very_long_strings.push(vls) } } @@ -2229,17 +2238,17 @@ pub struct Attribute { } impl Attribute { - fn parse<'a>(decoder: &Decoder, input: &'a str) -> Result<(Attribute, &'a str), Error> { + fn parse<'a>(decoder: &Decoder, input: &'a str) -> Result<(Attribute, &'a str), Warning> { let Some((name, mut input)) = input.split_once('(') else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let name = decoder .new_identifier(name) - .map_err(Error::InvalidAttributeName)?; + .map_err(Warning::InvalidAttributeName)?; let mut values = Vec::new(); loop { let Some((value, rest)) = input.split_once('\n') else { - return Err(Error::TBD); + return Err(Warning::TBD); }; if let Some(stripped) = value .strip_prefix('\'') @@ -2247,7 +2256,7 @@ impl Attribute { { values.push(stripped.into()); } else { - decoder.warn(Error::TBD); + decoder.warn(Warning::TBD); values.push(value.into()); } if let Some(rest) = rest.strip_prefix(')') { @@ -2267,7 +2276,7 @@ impl AttributeSet { decoder: &Decoder, mut input: &'a str, sentinel: Option, - ) -> Result<(AttributeSet, &'a str), Error> { + ) -> Result<(AttributeSet, &'a str), Warning> { let mut attributes = HashMap::new(); let rest = loop { match input.chars().next() { @@ -2297,10 +2306,10 @@ pub struct FileAttributeRecord(AttributeSet); impl FileAttributeRecord { fn decode(source: &TextRecord, decoder: &Decoder) -> Self { let input = decoder.decode(&source.text); - match AttributeSet::parse(decoder, &input, None).warn_on_error(&decoder.warn) { + match AttributeSet::parse(decoder, &input, None).issue_warning(&decoder.warn) { Some((set, rest)) => { if !rest.is_empty() { - decoder.warn(Error::TBD); + decoder.warn(Warning::TBD); } FileAttributeRecord(set) } @@ -2322,13 +2331,13 @@ pub struct VarAttributeSet { } impl VarAttributeSet { - fn parse<'a>(decoder: &Decoder, input: &'a str) -> Result<(VarAttributeSet, &'a str), Error> { + fn parse<'a>(decoder: &Decoder, input: &'a str) -> Result<(VarAttributeSet, &'a str), Warning> { let Some((long_var_name, rest)) = input.split_once(':') else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let long_var_name = decoder .new_identifier(long_var_name) - .map_err(Error::InvalidAttributeVariableName)?; + .map_err(Warning::InvalidAttributeVariableName)?; let (attributes, rest) = AttributeSet::parse(decoder, rest, Some('/'))?; let var_attribute = VarAttributeSet { long_var_name, @@ -2348,7 +2357,7 @@ impl VariableAttributeRecord { let mut var_attribute_sets = Vec::new(); while !input.is_empty() { let Some((var_attribute, rest)) = - VarAttributeSet::parse(decoder, &input).warn_on_error(&decoder.warn) + VarAttributeSet::parse(decoder, &input).issue_warning(&decoder.warn) else { break; }; @@ -2366,16 +2375,16 @@ pub struct LongName { } impl LongName { - fn parse(input: &str, decoder: &Decoder) -> Result { + fn parse(input: &str, decoder: &Decoder) -> Result { let Some((short_name, long_name)) = input.split_once('=') else { - return Err(Error::TBD); + return Err(Warning::TBD); }; let short_name = decoder .new_identifier(short_name) - .map_err(Error::InvalidShortName)?; + .map_err(Warning::InvalidShortName)?; let long_name = decoder .new_identifier(long_name) - .map_err(Error::InvalidLongName)?; + .map_err(Warning::InvalidLongName)?; Ok(LongName { short_name, long_name, @@ -2391,7 +2400,7 @@ impl LongNamesRecord { let input = decoder.decode(&source.text); let mut names = Vec::new(); for pair in input.split('\t').filter(|s| !s.is_empty()) { - if let Some(long_name) = LongName::parse(pair, decoder).warn_on_error(&decoder.warn) { + if let Some(long_name) = LongName::parse(pair, decoder).issue_warning(&decoder.warn) { names.push(long_name); } } @@ -2403,7 +2412,6 @@ impl LongNamesRecord { pub struct ProductInfoRecord(pub String); impl ProductInfoRecord { - const NAME: &'static str = "extra product info"; fn decode(source: &TextRecord, decoder: &Decoder) -> Self { Self(decoder.decode(&source.text).into()) } @@ -2415,14 +2423,14 @@ pub struct VariableSet { } impl VariableSet { - fn parse(input: &str, decoder: &Decoder) -> Result { - let (name, input) = input.split_once('=').ok_or(Error::TBD)?; + fn parse(input: &str, decoder: &Decoder) -> Result { + let (name, input) = input.split_once('=').ok_or(Warning::TBD)?; let mut vars = Vec::new(); for var in input.split_ascii_whitespace() { if let Some(identifier) = decoder .new_identifier(var) - .map_err(Error::InvalidVariableSetName) - .warn_on_error(&decoder.warn) + .map_err(Warning::InvalidVariableSetName) + .issue_warning(&decoder.warn) { vars.push(identifier); } @@ -2445,7 +2453,7 @@ impl VariableSetRecord { let mut sets = Vec::new(); let input = decoder.decode(&source.text); for line in input.lines() { - if let Some(set) = VariableSet::parse(line, decoder).warn_on_error(&decoder.warn) { + if let Some(set) = VariableSet::parse(line, decoder).issue_warning(&decoder.warn) { sets.push(set) } } @@ -2456,11 +2464,16 @@ impl VariableSetRecord { } } -trait WarnOnError { - fn warn_on_error(self, warn: &F) -> Option; +trait IssueWarning { + fn issue_warning(self, warn: &F) -> Option + where + F: Fn(Warning); } -impl WarnOnError for Result { - fn warn_on_error(self, warn: &F) -> Option { +impl IssueWarning for Result { + fn issue_warning(self, warn: &F) -> Option + where + F: Fn(Warning), + { match self { Ok(result) => Some(result), Err(error) => { @@ -2489,10 +2502,10 @@ pub struct Extension { } impl Extension { - fn check_size(&self) -> Result<(), Error> { + fn check_size(&self) -> Result<(), Warning> { if let Some(expected_size) = E::SIZE { if self.size != expected_size { - return Err(Error::BadRecordSize { + return Err(Warning::BadRecordSize { offset: self.offsets.start, record: E::NAME.into(), size: self.size, @@ -2502,7 +2515,7 @@ impl Extension { } if let Some(expected_count) = E::COUNT { if self.count != expected_count { - return Err(Error::BadRecordCount { + return Err(Warning::BadRecordCount { offset: self.offsets.start, record: E::NAME.into(), count: self.count, @@ -2517,7 +2530,7 @@ impl Extension { r: &mut R, endian: Endian, n_vars: usize, - warn: &Box, + warn: &Box, ) -> Result, Error> { let subtype = endian.parse(read_bytes(r)?); let header_offset = r.stream_position()?; @@ -2751,10 +2764,10 @@ impl LongStringValueLabels { fn decode<'a>( &'a self, decoder: &Decoder, - ) -> Result>, Error> { + ) -> Result>, Warning> { let var_name = decoder.decode(&self.var_name); let var_name = Identifier::new(var_name.trim_end(), decoder.encoding) - .map_err(Error::InvalidLongStringValueLabelName)?; + .map_err(Warning::InvalidLongStringValueLabelName)?; let mut labels = Vec::with_capacity(self.labels.len()); for (value, label) in self.labels.iter() { @@ -2783,7 +2796,7 @@ impl ExtensionRecord for LongStringValueLabelRecord { const COUNT: Option = None; const NAME: &'static str = "long string value labels record"; - fn parse(ext: &Extension, endian: Endian) -> Result { + fn parse(ext: &Extension, endian: Endian) -> Result { ext.check_size::()?; let mut input = &ext.data[..]; @@ -2814,7 +2827,7 @@ impl LongStringValueLabelRecord { fn decode<'a>( &'a self, decoder: &Decoder, - ) -> Result>, Error> { + ) -> LongStringValueLabelRecord> { let mut labels = Vec::with_capacity(self.0.len()); for label in &self.0 { match label.decode(decoder) { @@ -2822,6 +2835,6 @@ impl LongStringValueLabelRecord { Err(error) => decoder.warn(error), } } - Ok(LongStringValueLabelRecord(labels)) + LongStringValueLabelRecord(labels) } } -- 2.30.2