identifier::{Error as IdError, Identifier},
sys::raw::{
read_bytes, read_string, read_vec, DecodedRecord, Decoder, Error, ErrorDetails, Magic,
- RawDatum, RawStrArray, RawWidth, Record, UntypedDatum, VarTypes, Warning,
+ RawDatum, RawStrArray, RawWidth, Record, UntypedDatum, VarTypes, Warning, WarningDetails,
},
};
let n_cases = (header.n_cases < i32::MAX as u32 / 2).then_some(header.n_cases);
if header.bias != 100.0 && header.bias != 0.0 {
- warn(Warning::UnexpectedBias(header.bias));
+ warn(Warning::new(
+ Some(84..92),
+ WarningDetails::UnexpectedBias(header.bias),
+ ));
}
let creation_date = RawString(header.creation_date.into());
}
};
- Self::read_inner(r, raw_width, individual_values, has_range, endian, warn).map_err(
- |details| {
- Error::new(
- {
- let n = individual_values + if has_range { 2 } else { 0 };
- Some(offsets.start..offsets.end + 8 * n as u64)
- },
- details,
- )
- },
+ Self::read_inner(
+ r,
+ offsets.clone(),
+ raw_width,
+ individual_values,
+ has_range,
+ endian,
+ warn,
)
+ .map_err(|details| {
+ Error::new(
+ {
+ let n = individual_values + if has_range { 2 } else { 0 };
+ Some(offsets.start..offsets.end + 8 * n as u64)
+ },
+ details,
+ )
+ })
}
fn read_inner<R>(
r: &mut R,
+ offsets: Range<u64>,
raw_width: RawWidth,
individual_values: usize,
has_range: bool,
});
return Ok(Self::new(values, range).unwrap());
}
- Ok(VarWidth::String(_)) if range.is_some() => warn(Warning::MissingValueStringRange),
+ Ok(VarWidth::String(_)) if range.is_some() => warn(Warning::new(
+ Some(offsets),
+ WarningDetails::MissingValueStringRange,
+ )),
Ok(VarWidth::String(width)) => {
let width = width.min(8) as usize;
let values = values
.collect();
return Ok(Self::new(values, None).unwrap());
}
- Err(()) => warn(Warning::MissingValueContinuation),
+ Err(()) => warn(Warning::new(
+ Some(offsets),
+ WarningDetails::MissingValueContinuation,
+ )),
}
Ok(Self::default())
}
}
let n: u32 = endian.parse(read_bytes(r)?);
+ let n_offsets = index_offset + 4..index_offset + 8;
if n > Self::MAX_INDEXES {
return Err(Error::new(
- Some(index_offset + 4..index_offset + 8),
+ Some(n_offsets),
ErrorDetails::TooManyVarIndexes {
n,
max: Self::MAX_INDEXES,
},
));
} else if n == 0 {
- warn(Warning::NoVarIndexes {
- offset: index_offset,
- });
+ warn(Warning::new(Some(n_offsets), WarningDetails::NoVarIndexes));
return Ok(None);
}
invalid_indexes.push(index);
}
}
+ let index_offsets = index_offset..r.stream_position()?;
if !invalid_indexes.is_empty() {
- warn(Warning::InvalidVarIndexes {
- offset: index_offset,
- max: var_types.n_values(),
- invalid: invalid_indexes,
- });
+ warn(Warning::new(
+ Some(index_offsets.clone()),
+ WarningDetails::InvalidVarIndexes {
+ max: var_types.n_values(),
+ invalid: invalid_indexes,
+ },
+ ));
}
let Some(&first_index) = dict_indexes.first() else {
}
});
if !wrong_type_indexes.is_empty() {
- warn(Warning::MixedVarTypes {
- offset: index_offset,
- var_type,
- wrong_types: wrong_type_indexes,
- });
+ warn(Warning::new(
+ Some(index_offsets),
+ WarningDetails::MixedVarTypes {
+ var_type,
+ wrong_types: wrong_type_indexes,
+ },
+ ));
}
let labels = labels
};
impl IntegerInfoRecord {
- pub fn parse(ext: &Extension, endian: Endian) -> Result<Record, Warning> {
+ pub fn parse(ext: &Extension, endian: Endian) -> Result<Record, WarningDetails> {
ext.check_size(&INTEGER_INFO_RECORD)?;
let mut input = &ext.data[..];
};
impl FloatInfoRecord {
- pub fn parse(ext: &Extension, endian: Endian) -> Result<Record, Warning> {
+ pub fn parse(ext: &Extension, endian: Endian) -> Result<Record, WarningDetails> {
ext.check_size(&FLOAT_INFO_RECORD)?;
let mut input = &ext.data[..];
pub struct RawLongNamesRecord(TextRecord);
impl RawLongNamesRecord {
- pub fn parse(extension: Extension) -> Result<Record, Warning> {
+ pub fn parse(extension: Extension) -> Result<Record, WarningDetails> {
Ok(Record::LongNames(Self(TextRecord::parse(
extension,
"long names record",
let input = decoder.decode(&self.0.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).issue_warning(&mut decoder.warn)
+ if let Some(long_name) =
+ LongName::parse(pair, decoder).issue_warning(&self.0.offsets, &mut decoder.warn)
{
names.push(long_name);
}
}
impl TextRecord {
- pub fn parse(extension: Extension, name: &str) -> Result<TextRecord, Warning> {
+ pub fn parse(extension: Extension, name: &str) -> Result<TextRecord, WarningDetails> {
extension.check_size(&ExtensionRecord {
size: Some(1),
count: None,
}
impl VeryLongString {
- fn parse(decoder: &Decoder, input: &str) -> Result<VeryLongString, Warning> {
+ fn parse(decoder: &Decoder, input: &str) -> Result<VeryLongString, WarningDetails> {
let Some((short_name, length)) = input.split_once('=') else {
- return Err(Warning::VeryLongStringMissingDelimiter(input.into()));
+ return Err(WarningDetails::VeryLongStringMissingDelimiter(input.into()));
};
let short_name = decoder
.new_identifier(short_name)
.and_then(Identifier::must_be_ordinary)
- .map_err(Warning::InvalidLongStringName)?;
+ .map_err(WarningDetails::InvalidLongStringName)?;
let length = length
.parse()
- .map_err(|_| Warning::VeryLongStringInvalidLength(input.into()))?;
+ .map_err(|_| WarningDetails::VeryLongStringInvalidLength(input.into()))?;
Ok(VeryLongString { short_name, length })
}
}
pub struct VeryLongStringsRecord(pub Vec<VeryLongString>);
impl RawVeryLongStringsRecord {
- pub fn parse(extension: Extension) -> Result<Record, Warning> {
+ pub fn parse(extension: Extension) -> Result<Record, WarningDetails> {
Ok(Record::VeryLongStrings(Self(TextRecord::parse(
extension,
"very long strings record",
.map(|s| s.trim_start_matches('\t'))
.filter(|s| !s.is_empty())
{
- if let Some(vls) =
- VeryLongString::parse(decoder, tuple).issue_warning(&mut decoder.warn)
+ if let Some(vls) = VeryLongString::parse(decoder, tuple)
+ .issue_warning(&self.0.offsets, &mut decoder.warn)
{
very_long_strings.push(vls)
}
}
impl MultipleResponseType {
- fn parse(input: &[u8]) -> Result<(MultipleResponseType, &[u8]), Warning> {
+ fn parse(input: &[u8]) -> Result<(MultipleResponseType, &[u8]), WarningDetails> {
let (mr_type, input) = match input.split_first() {
Some((b'C', input)) => (MultipleResponseType::MultipleCategory, input),
Some((b'D', input)) => {
} else if let Some(rest) = input.strip_prefix(b" 11 ") {
(CategoryLabels::VarLabels, rest)
} else {
- return Err(Warning::InvalidMultipleDichotomyLabelType);
+ return Err(WarningDetails::InvalidMultipleDichotomyLabelType);
};
let (value, input) = parse_counted_string(input)?;
(
input,
)
}
- _ => return Err(Warning::InvalidMultipleResponseType),
+ _ => return Err(WarningDetails::InvalidMultipleResponseType),
};
Ok((mr_type, input))
}
}
impl MultipleResponseSet<RawString, RawString> {
- fn parse(input: &[u8]) -> Result<(Self, &[u8]), Warning> {
+ fn parse(input: &[u8]) -> Result<(Self, &[u8]), WarningDetails> {
let Some(equals) = input.iter().position(|&b| b == b'=') else {
- return Err(Warning::MultipleResponseSyntaxError("missing `=`"));
+ return Err(WarningDetails::MultipleResponseSyntaxError("missing `=`"));
};
let (name, input) = input.split_at(equals);
let input = input.strip_prefix(b"=").unwrap();
let (mr_type, input) = MultipleResponseType::parse(input)?;
let Some(input) = input.strip_prefix(b" ") else {
- return Err(Warning::MultipleResponseSyntaxError(
+ return Err(WarningDetails::MultipleResponseSyntaxError(
"missing space after multiple response type",
));
};
match input.split_first() {
Some((b' ', rest)) => {
let Some(length) = rest.iter().position(|b| b" \n".contains(b)) else {
- return Err(Warning::MultipleResponseSyntaxError(
+ return Err(WarningDetails::MultipleResponseSyntaxError(
"missing variable name delimiter",
));
};
input = rest;
}
_ => {
- return Err(Warning::MultipleResponseSyntaxError(
+ return Err(WarningDetails::MultipleResponseSyntaxError(
"missing space preceding variable name",
));
}
fn decode(
&self,
+ offsets: &Range<u64>,
decoder: &mut Decoder,
- ) -> Result<MultipleResponseSet<Identifier, String>, Warning> {
+ ) -> Result<MultipleResponseSet<Identifier, String>, WarningDetails> {
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(Warning::InvalidMrSetName)
- .issue_warning(&mut decoder.warn)
+ .map_err(WarningDetails::InvalidMrSetName)
+ .issue_warning(offsets, &mut decoder.warn)
{
short_names.push(short_name);
}
Ok(MultipleResponseSet {
name: decoder
.decode_identifier(&self.name)
- .map_err(Warning::InvalidMrSetVariableName)?,
+ .map_err(WarningDetails::InvalidMrSetVariableName)?,
label: decoder.decode(&self.label).to_string(),
mr_type: self.mr_type.clone(),
short_names,
}
#[derive(Clone, Debug)]
-pub struct MultipleResponseRecord<I, S>(pub Vec<MultipleResponseSet<I, S>>)
+pub struct MultipleResponseRecord<I, S>
where
I: Debug,
- S: Debug;
+ S: Debug,
+{
+ pub offsets: Range<u64>,
+ pub sets: Vec<MultipleResponseSet<I, S>>,
+}
static MULTIPLE_RESPONSE_RECORD: ExtensionRecord = ExtensionRecord {
size: Some(1),
};
impl MultipleResponseRecord<RawString, RawString> {
- fn parse(ext: &Extension, _endian: Endian) -> Result<Record, Warning> {
+ fn parse(ext: &Extension, _endian: Endian) -> Result<Record, WarningDetails> {
ext.check_size(&MULTIPLE_RESPONSE_RECORD)?;
let mut input = &ext.data[..];
sets.push(set);
input = rest;
}
- Ok(Record::MultipleResponse(MultipleResponseRecord(sets)))
+ Ok(Record::MultipleResponse(MultipleResponseRecord {
+ offsets: ext.offsets.clone(),
+ sets,
+ }))
}
}
impl MultipleResponseRecord<RawString, RawString> {
pub fn decode(self, decoder: &mut Decoder) -> DecodedRecord {
let mut sets = Vec::new();
- for set in self.0.iter() {
- if let Some(set) = set.decode(decoder).issue_warning(&mut decoder.warn) {
+ for set in self.sets.iter() {
+ if let Some(set) = set
+ .decode(&self.offsets, decoder)
+ .issue_warning(&self.offsets, &mut decoder.warn)
+ {
sets.push(set);
}
}
- DecodedRecord::MultipleResponse(MultipleResponseRecord(sets))
+ DecodedRecord::MultipleResponse(MultipleResponseRecord {
+ offsets: self.offsets,
+ sets,
+ })
}
}
-fn parse_counted_string(input: &[u8]) -> Result<(RawString, &[u8]), Warning> {
+fn parse_counted_string(input: &[u8]) -> Result<(RawString, &[u8]), WarningDetails> {
let Some(space) = input.iter().position(|&b| b == b' ') else {
- return Err(Warning::CountedStringMissingSpace);
+ return Err(WarningDetails::CountedStringMissingSpace);
};
let Ok(length) = from_utf8(&input[..space]) else {
- return Err(Warning::CountedStringInvalidUTF8);
+ return Err(WarningDetails::CountedStringInvalidUTF8);
};
let Ok(length): Result<usize, _> = length.parse() else {
- return Err(Warning::CountedStringInvalidLength(length.into()));
+ return Err(WarningDetails::CountedStringInvalidLength(length.into()));
};
let Some((string, rest)) = input[space + 1..].split_at_checked(length) else {
- return Err(Warning::CountedStringTooLong(length));
+ return Err(WarningDetails::CountedStringTooLong(length));
};
Ok((string.into(), rest))
}
impl Measure {
- fn try_decode(source: u32) -> Result<Option<Measure>, Warning> {
+ fn try_decode(source: u32) -> Result<Option<Measure>, WarningDetails> {
match source {
0 => Ok(None),
1 => Ok(Some(Measure::Nominal)),
2 => Ok(Some(Measure::Ordinal)),
3 => Ok(Some(Measure::Scale)),
- _ => Err(Warning::InvalidMeasurement(source)),
+ _ => Err(WarningDetails::InvalidMeasurement(source)),
}
}
}
impl Alignment {
- fn try_decode(source: u32) -> Result<Option<Alignment>, Warning> {
+ fn try_decode(source: u32) -> Result<Option<Alignment>, WarningDetails> {
match source {
0 => Ok(Some(Alignment::Left)),
1 => Ok(Some(Alignment::Right)),
2 => Ok(Some(Alignment::Center)),
- _ => Err(Warning::InvalidAlignment(source)),
+ _ => Err(WarningDetails::InvalidAlignment(source)),
}
}
}
var_types: &VarTypes,
endian: Endian,
warn: &mut dyn FnMut(Warning),
- ) -> Result<Record, Warning> {
+ ) -> Result<Record, WarningDetails> {
if ext.size != 4 {
- return Err(Warning::BadRecordSize {
- offset: ext.offsets.start,
+ return Err(WarningDetails::BadRecordSize {
record: String::from("variable display record"),
size: ext.size,
expected_size: 4,
} else if ext.count as usize == 2 * n_vars {
false
} else {
- return Err(Warning::InvalidVariableDisplayCount {
+ return Err(WarningDetails::InvalidVariableDisplayCount {
count: ext.count as usize,
first: 2 * n_vars,
second: 3 * n_vars,
let mut input = &ext.data[..];
for _ in 0..n_vars {
let measure = Measure::try_decode(endian.parse(read_bytes(&mut input).unwrap()))
- .issue_warning(warn)
+ .issue_warning(&ext.offsets, 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()))
- .issue_warning(warn)
+ .issue_warning(&ext.offsets, warn)
.flatten();
var_displays.push(VarDisplay {
measure,
}
#[derive(Clone, Debug)]
-pub struct LongStringMissingValueRecord<N>(pub Vec<LongStringMissingValues<N>>)
+pub struct LongStringMissingValueRecord<N>
where
- N: Debug;
+ N: Debug,
+{
+ pub offsets: Range<u64>,
+ pub values: Vec<LongStringMissingValues<N>>,
+}
static LONG_STRING_MISSING_VALUE_RECORD: ExtensionRecord = ExtensionRecord {
size: Some(1),
ext: &Extension,
endian: Endian,
warn: &mut dyn FnMut(Warning),
- ) -> Result<Record, Warning> {
+ ) -> Result<Record, WarningDetails> {
ext.check_size(&LONG_STRING_MISSING_VALUE_RECORD)?;
let mut input = &ext.data[..];
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;
- warn(Warning::BadLongMissingValueLength {
- record_offset: ext.offsets.start,
- offset,
- value_len,
- });
+ warn(Warning::new(
+ Some(ext.offsets.clone()),
+ WarningDetails::BadLongMissingValueLength { offset, value_len },
+ ));
read_vec(&mut input, value_len as usize * n_missing_values as usize)?;
continue;
}
});
}
Ok(Record::LongStringMissingValues(
- LongStringMissingValueRecord(missing_value_set),
+ LongStringMissingValueRecord {
+ offsets: ext.offsets.clone(),
+ values: missing_value_set,
+ },
))
}
}
impl LongStringMissingValueRecord<RawString> {
pub fn decode(self, decoder: &mut Decoder) -> LongStringMissingValueRecord<Identifier> {
- let mut mvs = Vec::with_capacity(self.0.len());
- for mv in self.0.iter() {
+ let mut mvs = Vec::with_capacity(self.values.len());
+ for mv in self.values.iter() {
if let Some(mv) = mv
.decode(decoder)
- .map_err(Warning::InvalidLongStringMissingValueVariableName)
- .issue_warning(&mut decoder.warn)
+ .map_err(WarningDetails::InvalidLongStringMissingValueVariableName)
+ .issue_warning(&self.offsets, &mut decoder.warn)
{
mvs.push(mv);
}
}
- LongStringMissingValueRecord(mvs)
+ LongStringMissingValueRecord {
+ offsets: self.offsets,
+ values: mvs,
+ }
}
}
};
impl EncodingRecord {
- fn parse(ext: &Extension, _endian: Endian) -> Result<Record, Warning> {
+ fn parse(ext: &Extension, _endian: Endian) -> Result<Record, WarningDetails> {
ext.check_size(&ENCODING_RECORD)?;
Ok(Record::Encoding(EncodingRecord(
- String::from_utf8(ext.data.clone()).map_err(|_| Warning::BadEncodingName {
- offset: ext.offsets.start,
- })?,
+ String::from_utf8(ext.data.clone()).map_err(|_| WarningDetails::BadEncodingName)?,
)))
}
}
};
impl NumberOfCasesRecord {
- fn parse(ext: &Extension, endian: Endian) -> Result<Record, Warning> {
+ fn parse(ext: &Extension, endian: Endian) -> Result<Record, WarningDetails> {
ext.check_size(&NUMBER_OF_CASES_RECORD)?;
let mut input = &ext.data[..];
pub struct RawVariableSetRecord(TextRecord);
impl RawVariableSetRecord {
- fn parse(extension: Extension) -> Result<Record, Warning> {
+ fn parse(extension: Extension) -> Result<Record, WarningDetails> {
Ok(Record::VariableSets(Self(TextRecord::parse(
extension,
"variable sets record",
let mut sets = Vec::new();
let input = decoder.decode(&self.0.text);
for line in input.lines() {
- if let Some(set) = VariableSet::parse(line, decoder).issue_warning(&mut decoder.warn) {
+ if let Some(set) = VariableSet::parse(line, decoder, &self.0.offsets)
+ .issue_warning(&self.0.offsets, &mut decoder.warn)
+ {
sets.push(set)
}
}
pub struct RawProductInfoRecord(TextRecord);
impl RawProductInfoRecord {
- fn parse(extension: Extension) -> Result<Record, Warning> {
+ fn parse(extension: Extension) -> Result<Record, WarningDetails> {
Ok(Record::ProductInfo(Self(TextRecord::parse(
extension,
"product info record",
}
impl Attribute {
- fn parse<'a>(decoder: &mut Decoder, input: &'a str) -> Result<(Attribute, &'a str), Warning> {
+ fn parse<'a>(
+ decoder: &mut Decoder,
+ offsets: &Range<u64>,
+ input: &'a str,
+ ) -> Result<(Attribute, &'a str), WarningDetails> {
let Some((name, mut input)) = input.split_once('(') else {
- return Err(Warning::AttributeMissingLParen(input.into()));
+ return Err(WarningDetails::AttributeMissingLParen(input.into()));
};
let name = decoder
.new_identifier(name)
- .map_err(Warning::InvalidAttributeName)?;
+ .map_err(WarningDetails::InvalidAttributeName)?;
let mut values = Vec::new();
loop {
let Some((value, rest)) = input.split_once('\n') else {
- return Err(Warning::AttributeMissingValue {
+ return Err(WarningDetails::AttributeMissingValue {
name: name.clone(),
index: values.len(),
});
{
values.push(stripped.into());
} else {
- decoder.warn(Warning::AttributeMissingQuotes {
- name: name.clone(),
- index: values.len(),
- });
+ decoder.warn(Warning::new(
+ Some(offsets.clone()),
+ WarningDetails::AttributeMissingQuotes {
+ name: name.clone(),
+ index: values.len(),
+ },
+ ));
values.push(value.into());
}
if let Some(rest) = rest.strip_prefix(')') {
impl Attributes {
fn parse<'a>(
decoder: &mut Decoder,
+ offsets: &Range<u64>,
mut input: &'a str,
sentinel: Option<char>,
- ) -> Result<(Attributes, &'a str, Vec<Identifier>), Warning> {
+ ) -> Result<(Attributes, &'a str, Vec<Identifier>), WarningDetails> {
let mut attributes = BTreeMap::new();
let mut duplicates = Vec::new();
let rest = loop {
None => break input,
c if c == sentinel => break &input[1..],
_ => {
- let (attribute, rest) = Attribute::parse(decoder, input)?;
+ let (attribute, rest) = Attribute::parse(decoder, offsets, input)?;
if attributes.contains_key(&attribute.name) {
duplicates.push(attribute.name.clone());
}
pub struct FileAttributesRecord(pub Attributes);
impl RawFileAttributesRecord {
- fn parse(extension: Extension) -> Result<Record, Warning> {
+ fn parse(extension: Extension) -> Result<Record, WarningDetails> {
Ok(Record::FileAttributes(Self(TextRecord::parse(
extension,
"file attributes record",
}
pub fn decode(self, decoder: &mut Decoder) -> FileAttributesRecord {
let input = decoder.decode(&self.0.text);
- match Attributes::parse(decoder, &input, None).issue_warning(&mut decoder.warn) {
+ match Attributes::parse(decoder, &self.0.offsets, &input, None)
+ .issue_warning(&self.0.offsets, &mut decoder.warn)
+ {
Some((set, rest, duplicates)) => {
if !duplicates.is_empty() {
- decoder.warn(Warning::DuplicateFileAttributes {
- attributes: duplicates,
- });
+ decoder.warn(Warning::new(
+ Some(self.0.offsets.clone()),
+ WarningDetails::DuplicateFileAttributes {
+ attributes: duplicates,
+ },
+ ));
}
if !rest.is_empty() {
- decoder.warn(dbg!(Warning::TBD));
+ decoder.warn(Warning::new(
+ Some(self.0.offsets.clone()),
+ WarningDetails::TBD,
+ ));
}
FileAttributesRecord(set)
}
impl VarAttributes {
fn parse<'a>(
decoder: &mut Decoder,
+ offsets: &Range<u64>,
input: &'a str,
- ) -> Result<(VarAttributes, &'a str), Warning> {
+ ) -> Result<(VarAttributes, &'a str), WarningDetails> {
let Some((long_var_name, rest)) = input.split_once(':') else {
- return Err(dbg!(Warning::TBD));
+ return Err(dbg!(WarningDetails::TBD));
};
let long_var_name = decoder
.new_identifier(long_var_name)
.and_then(Identifier::must_be_ordinary)
- .map_err(Warning::InvalidAttributeVariableName)?;
- let (attributes, rest, duplicates) = Attributes::parse(decoder, rest, Some('/'))?;
+ .map_err(WarningDetails::InvalidAttributeVariableName)?;
+ let (attributes, rest, duplicates) = Attributes::parse(decoder, offsets, rest, Some('/'))?;
if !duplicates.is_empty() {
- decoder.warn(Warning::DuplicateVariableAttributes {
- variable: long_var_name.clone(),
- attributes: duplicates,
- });
+ decoder.warn(Warning::new(
+ Some(offsets.clone()),
+ WarningDetails::DuplicateVariableAttributes {
+ variable: long_var_name.clone(),
+ attributes: duplicates,
+ },
+ ));
}
let var_attribute = VarAttributes {
long_var_name,
pub struct VariableAttributesRecord(pub Vec<VarAttributes>);
impl RawVariableAttributesRecord {
- fn parse(extension: Extension) -> Result<Record, Warning> {
+ fn parse(extension: Extension) -> Result<Record, WarningDetails> {
Ok(Record::VariableAttributes(Self(TextRecord::parse(
extension,
"variable attributes record",
let mut input = decoded.as_ref();
let mut var_attribute_sets = Vec::new();
while !input.is_empty() {
- let Some((var_attribute, rest)) =
- VarAttributes::parse(decoder, input).issue_warning(&mut decoder.warn)
+ let Some((var_attribute, rest)) = VarAttributes::parse(decoder, &self.0.offsets, input)
+ .issue_warning(&self.0.offsets, &mut decoder.warn)
else {
break;
};
}
impl LongName {
- fn parse(input: &str, decoder: &Decoder) -> Result<Self, Warning> {
+ fn parse(input: &str, decoder: &Decoder) -> Result<Self, WarningDetails> {
let Some((short_name, long_name)) = input.split_once('=') else {
- return Err(Warning::LongNameMissingEquals);
+ return Err(WarningDetails::LongNameMissingEquals);
};
let short_name = decoder
.new_identifier(short_name)
.and_then(Identifier::must_be_ordinary)
- .map_err(Warning::InvalidShortName)?;
+ .map_err(WarningDetails::InvalidShortName)?;
let long_name = decoder
.new_identifier(long_name)
.and_then(Identifier::must_be_ordinary)
- .map_err(Warning::InvalidLongName)?;
+ .map_err(WarningDetails::InvalidLongName)?;
Ok(LongName {
short_name,
long_name,
}
impl VariableSet {
- fn parse(input: &str, decoder: &mut Decoder) -> Result<Self, Warning> {
+ fn parse(
+ input: &str,
+ decoder: &mut Decoder,
+ offsets: &Range<u64>,
+ ) -> Result<Self, WarningDetails> {
let (name, input) = input
.split_once('=')
- .ok_or(Warning::VariableSetMissingEquals)?;
+ .ok_or(WarningDetails::VariableSetMissingEquals)?;
let mut vars = Vec::new();
for var in input.split_ascii_whitespace() {
if let Some(identifier) = decoder
.new_identifier(var)
.and_then(Identifier::must_be_ordinary)
- .map_err(Warning::InvalidVariableSetName)
- .issue_warning(&mut decoder.warn)
+ .map_err(WarningDetails::InvalidVariableSetName)
+ .issue_warning(offsets, &mut decoder.warn)
{
vars.push(identifier);
}
}
trait IssueWarning<T> {
- fn issue_warning(self, warn: &mut dyn FnMut(Warning)) -> Option<T>;
+ fn issue_warning(self, offsets: &Range<u64>, warn: &mut dyn FnMut(Warning)) -> Option<T>;
}
-impl<T> IssueWarning<T> for Result<T, Warning> {
- fn issue_warning(self, warn: &mut dyn FnMut(Warning)) -> Option<T> {
+impl<T> IssueWarning<T> for Result<T, WarningDetails> {
+ fn issue_warning(self, offsets: &Range<u64>, warn: &mut dyn FnMut(Warning)) -> Option<T> {
match self {
Ok(result) => Some(result),
Err(error) => {
- warn(error);
+ warn(Warning::new(Some(offsets.clone()), error));
None
}
}
}
impl Extension {
- pub fn check_size(&self, expected: &ExtensionRecord) -> Result<(), Warning> {
+ pub fn check_size(&self, expected: &ExtensionRecord) -> Result<(), WarningDetails> {
match expected.size {
Some(expected_size) if self.size != expected_size => {
- return Err(Warning::BadRecordSize {
- offset: self.offsets.start,
+ return Err(WarningDetails::BadRecordSize {
record: expected.name.into(),
size: self.size,
expected_size,
}
match expected.count {
Some(expected_count) if self.count != expected_count => {
- return Err(Warning::BadRecordCount {
- offset: self.offsets.start,
+ return Err(WarningDetails::BadRecordCount {
record: expected.name.into(),
count: self.count,
expected_count,
let start_offset = r.stream_position()?;
let data = read_vec(r, product as usize)?;
let end_offset = start_offset + product as u64;
+ let offsets = start_offset..end_offset;
let extension = Extension {
- offsets: start_offset..end_offset,
+ offsets: offsets.clone(),
subtype,
size,
count,
};
match result {
Ok(result) => Ok(Some(result)),
- Err(error) => {
- warn(error);
+ Err(details) => {
+ warn(Warning::new(Some(offsets), details));
Ok(None)
}
}
fn decode(
&self,
decoder: &mut Decoder,
- ) -> Result<LongStringValueLabels<Identifier, String>, Warning> {
+ ) -> Result<LongStringValueLabels<Identifier, String>, WarningDetails> {
let var_name = decoder.decode(&self.var_name);
let var_name = Identifier::from_encoding(var_name.trim_end(), decoder.encoding)
- .map_err(Warning::InvalidLongStringValueLabelName)?;
+ .map_err(WarningDetails::InvalidLongStringValueLabelName)?;
let mut labels = Vec::with_capacity(self.labels.len());
for (value, label) in self.labels.iter() {
}
#[derive(Clone, Debug)]
-pub struct LongStringValueLabelRecord<N, S>(pub Vec<LongStringValueLabels<N, S>>)
+pub struct LongStringValueLabelRecord<N, S>
where
N: Debug,
- S: Debug;
+ S: Debug,
+{
+ pub offsets: Range<u64>,
+ pub labels: Vec<LongStringValueLabels<N, S>>,
+}
static LONG_STRING_VALUE_LABEL_RECORD: ExtensionRecord = ExtensionRecord {
size: Some(1),
};
impl LongStringValueLabelRecord<RawString, RawString> {
- fn parse(ext: &Extension, endian: Endian) -> Result<Record, Warning> {
+ fn parse(ext: &Extension, endian: Endian) -> Result<Record, WarningDetails> {
ext.check_size(&LONG_STRING_VALUE_LABEL_RECORD)?;
let mut input = &ext.data[..];
labels,
})
}
- Ok(Record::LongStringValueLabels(LongStringValueLabelRecord(
- label_set,
- )))
+ Ok(Record::LongStringValueLabels(LongStringValueLabelRecord {
+ offsets: ext.offsets.clone(),
+ labels: label_set,
+ }))
}
}
impl LongStringValueLabelRecord<RawString, RawString> {
pub fn decode(self, decoder: &mut Decoder) -> LongStringValueLabelRecord<Identifier, String> {
- let mut labels = Vec::with_capacity(self.0.len());
- for label in &self.0 {
+ let mut labels = Vec::with_capacity(self.labels.len());
+ for label in &self.labels {
match label.decode(decoder) {
Ok(set) => labels.push(set),
- Err(error) => decoder.warn(error),
+ Err(error) => decoder.warn(Warning::new(Some(self.offsets.clone()), error)),
}
}
- LongStringValueLabelRecord(labels)
+ LongStringValueLabelRecord {
+ offsets: self.offsets,
+ labels,
+ }
}
}
let mut expected_uncmp_ofs = zheader.zheader_offset;
let mut expected_cmp_ofs = zheader.zheader_offset + 24;
for (index, block) in blocks.iter().enumerate() {
+ let block_start = start_offset + 24 + 24 * index as u64;
+ let block_offsets = block_start..block_start + 24;
+
if block.uncompressed_ofs != expected_uncmp_ofs {
Err(ErrorDetails::ZlibTrailerBlockWrongUncmpOfs {
index,
} else {
Ok(())
}
- .map_err(|details| {
- Error::new(
- {
- let block_start = start_offset + 24 + 24 * index as u64;
- Some(block_start..block_start + 24)
- },
- details,
- )
- })?;
+ .map_err(|details| Error::new(Some(block_offsets.clone()), details))?;
if index < blocks.len() - 1 {
if block.uncompressed_size != block_size {
- warn(Warning::ZlibTrailerBlockWrongSize {
- index,
- actual: block.uncompressed_size,
- expected: block_size,
- });
+ warn(Warning::new(
+ Some(block_offsets),
+ WarningDetails::ZlibTrailerBlockWrongSize {
+ index,
+ actual: block.uncompressed_size,
+ expected: block_size,
+ },
+ ));
}
} else {
if block.uncompressed_size > block_size {
- warn(Warning::ZlibTrailerBlockTooBig {
- index,
- actual: block.uncompressed_size,
- max_expected: block_size,
- });
+ warn(Warning::new(
+ Some(block_offsets),
+ WarningDetails::ZlibTrailerBlockTooBig {
+ index,
+ actual: block.uncompressed_size,
+ max_expected: block_size,
+ },
+ ));
}
}