impl std::error::Error for Warning {}
impl Warning {
- pub fn new(offsets: Option<Range<u64>>, details: WarningDetails) -> Self {
- Self { offsets, details }
+ pub fn new(offsets: Option<Range<u64>>, details: impl Into<WarningDetails>) -> Self {
+ Self {
+ offsets,
+ details: details.into(),
+ }
}
}
}
}
+/// Details of a [Warning].
#[derive(ThisError, Debug)]
pub enum WarningDetails {
- #[error("Unexpected end of data inside extension record.")]
- UnexpectedEndOfData,
+ /// Warning for file header.
+ #[error("In file header: {0}")]
+ Header(#[from] HeaderWarning),
- #[error(
- "At least one valid variable index for value labels is required but none were specified."
- )]
- NoVarIndexes,
+ /// Warning for variable records.
+ #[error("In variable record: {0}")]
+ Variable(#[from] VariableWarning),
- #[error("The first variable index is for a {var_type} variable but the following variable indexes are for {} variables: {wrong_types:?}", !var_type)]
- MixedVarTypes {
- var_type: VarType,
- wrong_types: Vec<u32>,
- },
+ /// Warning for extension records.
+ #[error("In extension record: {0}")]
+ Extension(#[from] ExtensionWarning),
- #[error(
- "One or more variable indexes for value labels were not in the valid range [1,{max}] or referred to string continuations: {invalid:?}"
- )]
- InvalidVarIndexes { max: usize, invalid: Vec<u32> },
+ /// Warning for value labels.
+ #[error("In value label record: {0}")]
+ ValueLabel(#[from] ValueLabelWarning),
+
+ /// Warning for long string missing values.
+ #[error("In long string missing values record: {0}")]
+ LongStringMissingValues(#[from] LongStringMissingValuesWarning),
+
+ /// Warning for long string value labels.
+ #[error("In long string value label record: {0}")]
+ LongStringValueLabel(#[from] LongStringValueLabelWarning),
+
+ /// Warning for long variable names.
+ #[error("In long variable name record: {0}")]
+ LongName(#[from] LongNameWarning),
+
+ /// Warning for very long strings.
+ #[error("In very long string record: {0}")]
+ VeryLongString(#[from] VeryLongStringWarning),
+
+ /// Warning for multiple response record.
+ #[error("In multiple response set record: {0}")]
+ MultipleResponse(#[from] MultipleResponseWarning),
+
+ /// Warning for attribute record.
+ #[error("In file or variable attribute record: {0}")]
+ Attribute(#[from] AttributeWarning),
+
+ /// Warning for variable display record.
+ #[error("In variable display record: {0}")]
+ VariableDisplay(#[from] VariableDisplayWarning),
+
+ /// Warning for variable set record.
+ #[error("In variable set record: {0}")]
+ VariableSet(#[from] VariableSetWarning),
+
+ /// Warning for ZLIB trailer.
+ #[error("In ZLIB trailer: {0}")]
+ ZlibTrailer(#[from] ZlibTrailerWarning),
+
+ #[error("Encoding record contains an encoding name that is not valid UTF-8.")]
+ BadEncodingName,
+
+ // XXX This is risky because `text` might be arbitarily long.
+ #[error("Text string contains invalid bytes for {encoding} encoding: {text:?}")]
+ MalformedString { encoding: String, text: String },
+
+ #[error("{0}")]
+ EncodingError(EncodingError),
+}
+
+impl From<IoError> for WarningDetails {
+ fn from(_source: IoError) -> Self {
+ Self::Extension(ExtensionWarning::UnexpectedEndOfData)
+ }
+}
+
+#[derive(ThisError, Debug)]
+pub enum HeaderWarning {
+ #[error("Compression bias is {0} instead of the usual values of 0 or 100.")]
+ UnexpectedBias(f64),
+}
+#[derive(ThisError, Debug)]
+pub enum VariableWarning {
+ #[error("Missing value record with range not allowed for string variable")]
+ MissingValueStringRange,
+
+ #[error("Missing value not allowed for long string continuation")]
+ MissingValueContinuation,
+}
+
+#[derive(ThisError, Debug)]
+pub enum ExtensionWarning {
+ /// Unexpected end of data.
+ #[error("Unexpected end of data.")]
+ UnexpectedEndOfData,
+
+ /// Invalid record size.
#[error("{record} has bad size {size} bytes instead of the expected {expected_size}.")]
BadRecordSize {
record: String,
expected_size: u32,
},
+ /// Invalid record count.
#[error("{record} has bad count {count} instead of the expected {expected_count}.")]
BadRecordCount {
record: String,
count: u32,
expected_count: u32,
},
+}
- #[error(
- "In long string missing values record, value length at offset {offset:#x} is {value_len} instead of the expected 8."
- )]
- BadLongMissingValueLength { offset: u64, value_len: u32 },
-
- #[error("Encoding record contains an encoding name that is not valid UTF-8.")]
- BadEncodingName,
+#[derive(ThisError, Debug)]
+pub enum ValueLabelWarning {
+ /// At least one valid variable index for value labels is required but none were specified.
+ #[error("At least one valid variable index is required but none were specified.")]
+ NoVarIndexes,
- // XXX This is risky because `text` might be arbitarily long.
- #[error("Text string contains invalid bytes for {encoding} encoding: {text:?}")]
- MalformedString { encoding: String, text: String },
+ /// Mixed variable types in value label record.
+ #[error("First variable index is for a {var_type} variable but the following variable indexes are for {} variables: {wrong_types:?}", !var_type)]
+ MixedVarTypes {
+ var_type: VarType,
+ wrong_types: Vec<u32>,
+ },
- #[error("Invalid variable measurement level value {0}")]
- InvalidMeasurement(u32),
+ /// Value label invalid variable indexes.
+ #[error(
+ "One or more variable indexes were not in the valid range [1,{max}] or referred to string continuations: {invalid:?}"
+ )]
+ InvalidVarIndexes { max: usize, invalid: Vec<u32> },
+}
- #[error("Invalid variable display alignment value {0}")]
- InvalidAlignment(u32),
+#[derive(ThisError, Debug)]
+pub enum LongStringMissingValuesWarning {
+ #[error("Value length at offset {offset:#x} is {value_len} instead of the expected 8.")]
+ BadValueLength { offset: u64, value_len: u32 },
- #[error("Invalid attribute name. {0}")]
- InvalidAttributeName(IdError),
+ #[error("Invalid variable name. {0}")]
+ InvalidVariableName(IdError),
+}
- #[error("Invalid variable name in attribute record. {0}")]
- InvalidAttributeVariableName(IdError),
+#[derive(ThisError, Debug)]
+pub enum LongStringValueLabelWarning {
+ #[error("Invalid variable name. {0}")]
+ InvalidVariableName(IdError),
+}
- #[error("Missing `=` separator in long variable name record.")]
+#[derive(ThisError, Debug)]
+pub enum LongNameWarning {
+ #[error("Missing `=` separator.")]
LongNameMissingEquals,
- #[error("Invalid short name in long variable name record. {0}")]
+ #[error("Invalid short name. {0}")]
InvalidShortName(IdError),
- #[error("Invalid name in long variable name record. {0}")]
+ #[error("Invalid long name. {0}")]
InvalidLongName(IdError),
+}
- #[error("Invalid variable name in very long string record. {0}")]
+#[derive(ThisError, Debug)]
+pub enum VeryLongStringWarning {
+ #[error("Invalid variable name. {0}")]
InvalidLongStringName(IdError),
- #[error("Invalid variable name in variable set record. {0}")]
- InvalidVariableSetName(IdError),
+ #[error("Missing delimiter in {0:?}.")]
+ VeryLongStringMissingDelimiter(String),
- #[error("Variable set missing name delimiter.")]
- VariableSetMissingEquals,
+ #[error("Invalid length in {0:?}.")]
+ VeryLongStringInvalidLength(String),
+}
+#[derive(ThisError, Debug)]
+pub enum MultipleResponseWarning {
#[error("Invalid multiple response set name. {0}")]
InvalidMrSetName(IdError),
- #[error("Invalid multiple response set variable name. {0}")]
+ #[error("Invalid variable name. {0}")]
InvalidMrSetVariableName(IdError),
- #[error("Invalid variable name in long string missing values record. {0}")]
- InvalidLongStringMissingValueVariableName(IdError),
-
- #[error("Invalid variable name in long string value label record. {0}")]
- InvalidLongStringValueLabelName(IdError),
-
- #[error("{0}")]
- EncodingError(EncodingError),
-
- #[error("Missing value record with range not allowed for string variable")]
- MissingValueStringRange,
-
- #[error("Missing value not allowed for long string continuation")]
- MissingValueContinuation,
-
- #[error("Invalid multiple dichotomy label type")]
+ #[error("Invalid multiple dichotomy label type.")]
InvalidMultipleDichotomyLabelType,
- #[error("Invalid multiple response type")]
+ #[error("Invalid multiple response type.")]
InvalidMultipleResponseType,
- #[error("Syntax error in multiple response record ({0})")]
+ #[error("Syntax error ({0}).")]
MultipleResponseSyntaxError(&'static str),
- #[error("Syntax error parsing counted string (missing trailing space)")]
+ #[error("Syntax error parsing counted string (missing trailing space).")]
CountedStringMissingSpace,
- #[error("Syntax error parsing counted string (invalid UTF-8)")]
+ #[error("Syntax error parsing counted string (invalid UTF-8).")]
CountedStringInvalidUTF8,
- #[error("Syntax error parsing counted string (invalid length {0:?})")]
+ #[error("Syntax error parsing counted string (invalid length {0:?}).")]
CountedStringInvalidLength(String),
- #[error("Syntax error parsing counted string (length {0:?} goes past end of input)")]
+ #[error("Syntax error parsing counted string (length {0:?} goes past end of input).")]
CountedStringTooLong(usize),
+}
- #[error(
- "Variable display record contains {count} items but should contain either {first} or {second}."
- )]
- InvalidVariableDisplayCount {
- count: usize,
- first: usize,
- second: usize,
- },
-
- #[error("Very long string record missing delimiter in {0:?}.")]
- VeryLongStringMissingDelimiter(String),
+#[derive(ThisError, Debug)]
+pub enum AttributeWarning {
+ #[error("Invalid attribute name. {0}")]
+ InvalidAttributeName(IdError),
- #[error("Very long string record has invalid length in {0:?}.")]
- VeryLongStringInvalidLength(String),
+ #[error("Invalid variable name in attribute record. {0}")]
+ InvalidAttributeVariableName(IdError),
#[error("Attribute record missing left parenthesis, in {0:?}.")]
AttributeMissingLParen(String),
#[error("File attributes record contains trailing garbage.")]
FileAttributesTrailingGarbage,
+}
- #[error("Compression bias is {0} instead of the usual values of 0 or 100.")]
- UnexpectedBias(f64),
+#[derive(ThisError, Debug)]
+pub enum VariableDisplayWarning {
+ #[error("Record contains {count} items but should contain either {first} or {second}.")]
+ InvalidVariableDisplayCount {
+ count: usize,
+ first: usize,
+ second: usize,
+ },
+ #[error("Invalid variable measurement level value {0}.")]
+ InvalidMeasurement(u32),
+
+ #[error("Invalid variable display alignment value {0}.")]
+ InvalidAlignment(u32),
+}
+
+#[derive(ThisError, Debug)]
+pub enum VariableSetWarning {
+ #[error("Invalid variable name. {0}")]
+ InvalidVariableSetName(IdError),
+
+ #[error("Missing name delimiter.")]
+ VariableSetMissingEquals,
+}
+
+#[derive(ThisError, Debug)]
+pub enum ZlibTrailerWarning {
#[error(
"ZLIB block descriptor {index} reported block size {actual:#x}, when {expected:#x} was expected."
)]
},
}
-impl From<IoError> for WarningDetails {
- fn from(_source: IoError) -> Self {
- Self::UnexpectedEndOfData
- }
-}
-
/// A raw record in a system file.
#[allow(missing_docs)] // Don't warn for missing docs on tuple members.
#[derive(Clone, Debug)]
}
}
+/// System file type, inferred from its "magic number".
+///
+/// The magic number is the first four bytes of the file.
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum Magic {
/// Regular system file.
}
#[derive(Default)]
-pub struct VarTypes {
- pub types: Vec<Option<VarWidth>>,
+struct VarTypes {
+ types: Vec<Option<VarWidth>>,
}
impl VarTypes {
endian::{Endian, Parse},
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, WarningDetails,
+ read_bytes, read_string, read_vec, AttributeWarning, DecodedRecord, Decoder, Error,
+ ErrorDetails, ExtensionWarning, HeaderWarning, LongNameWarning,
+ LongStringMissingValuesWarning, LongStringValueLabelWarning, Magic,
+ MultipleResponseWarning, RawDatum, RawStrArray, RawWidth, Record, UntypedDatum,
+ ValueLabelWarning, VarTypes, VariableDisplayWarning, VariableSetWarning, VariableWarning,
+ VeryLongStringWarning, Warning, WarningDetails, ZlibTrailerWarning,
},
};
if header.bias != 100.0 && header.bias != 0.0 {
warn(Warning::new(
Some(84..92),
- WarningDetails::UnexpectedBias(header.bias),
+ HeaderWarning::UnexpectedBias(header.bias),
));
}
}
Ok(VarWidth::String(_)) if range.is_some() => warn(Warning::new(
Some(offsets),
- WarningDetails::MissingValueStringRange,
+ VariableWarning::MissingValueStringRange,
)),
Ok(VarWidth::String(width)) => {
let width = width.min(8) as usize;
}
Err(()) => warn(Warning::new(
Some(offsets),
- WarningDetails::MissingValueContinuation,
+ VariableWarning::MissingValueContinuation,
)),
}
Ok(Self::default())
}
impl ValueLabelRecord<RawDatum, RawString> {
- pub fn read<R: Read + Seek>(
+ pub(super) fn read<R: Read + Seek>(
r: &mut R,
endian: Endian,
var_types: &VarTypes,
},
));
} else if n == 0 {
- warn(Warning::new(Some(n_offsets), WarningDetails::NoVarIndexes));
+ warn(Warning::new(
+ Some(n_offsets),
+ ValueLabelWarning::NoVarIndexes,
+ ));
return Ok(None);
}
if !invalid_indexes.is_empty() {
warn(Warning::new(
Some(index_offsets.clone()),
- WarningDetails::InvalidVarIndexes {
+ ValueLabelWarning::InvalidVarIndexes {
max: var_types.n_values(),
invalid: invalid_indexes,
},
if !wrong_type_indexes.is_empty() {
warn(Warning::new(
Some(index_offsets),
- WarningDetails::MixedVarTypes {
+ ValueLabelWarning::MixedVarTypes {
var_type,
wrong_types: wrong_type_indexes,
},
impl VeryLongString {
fn parse(decoder: &Decoder, input: &str) -> Result<VeryLongString, WarningDetails> {
let Some((short_name, length)) = input.split_once('=') else {
- return Err(WarningDetails::VeryLongStringMissingDelimiter(input.into()));
+ return Err(VeryLongStringWarning::VeryLongStringMissingDelimiter(input.into()).into());
};
let short_name = decoder
.new_identifier(short_name)
.and_then(Identifier::must_be_ordinary)
- .map_err(WarningDetails::InvalidLongStringName)?;
+ .map_err(VeryLongStringWarning::InvalidLongStringName)?;
let length = length
.parse()
- .map_err(|_| WarningDetails::VeryLongStringInvalidLength(input.into()))?;
+ .map_err(|_| VeryLongStringWarning::VeryLongStringInvalidLength(input.into()))?;
Ok(VeryLongString { short_name, length })
}
}
} else if let Some(rest) = input.strip_prefix(b" 11 ") {
(CategoryLabels::VarLabels, rest)
} else {
- return Err(WarningDetails::InvalidMultipleDichotomyLabelType);
+ return Err(MultipleResponseWarning::InvalidMultipleDichotomyLabelType.into());
};
let (value, input) = parse_counted_string(input)?;
(
input,
)
}
- _ => return Err(WarningDetails::InvalidMultipleResponseType),
+ _ => return Err(MultipleResponseWarning::InvalidMultipleResponseType.into()),
};
Ok((mr_type, input))
}
impl MultipleResponseSet<RawString, RawString> {
fn parse(input: &[u8]) -> Result<(Self, &[u8]), WarningDetails> {
let Some(equals) = input.iter().position(|&b| b == b'=') else {
- return Err(WarningDetails::MultipleResponseSyntaxError("missing `=`"));
+ return Err(MultipleResponseWarning::MultipleResponseSyntaxError("missing `=`").into());
};
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(WarningDetails::MultipleResponseSyntaxError(
+ return Err(MultipleResponseWarning::MultipleResponseSyntaxError(
"missing space after multiple response type",
- ));
+ )
+ .into());
};
let (label, mut input) = parse_counted_string(input)?;
let mut vars = Vec::new();
match input.split_first() {
Some((b' ', rest)) => {
let Some(length) = rest.iter().position(|b| b" \n".contains(b)) else {
- return Err(WarningDetails::MultipleResponseSyntaxError(
+ return Err(MultipleResponseWarning::MultipleResponseSyntaxError(
"missing variable name delimiter",
- ));
+ )
+ .into());
};
let (var, rest) = rest.split_at(length);
if !var.is_empty() {
input = rest;
}
_ => {
- return Err(WarningDetails::MultipleResponseSyntaxError(
+ return Err(MultipleResponseWarning::MultipleResponseSyntaxError(
"missing space preceding variable name",
- ));
+ )
+ .into());
}
}
}
for short_name in self.short_names.iter() {
if let Some(short_name) = decoder
.decode_identifier(short_name)
- .map_err(WarningDetails::InvalidMrSetName)
+ .map_err(MultipleResponseWarning::InvalidMrSetName)
.issue_warning(offsets, &mut decoder.warn)
{
short_names.push(short_name);
Ok(MultipleResponseSet {
name: decoder
.decode_identifier(&self.name)
- .map_err(WarningDetails::InvalidMrSetVariableName)?,
+ .map_err(MultipleResponseWarning::InvalidMrSetVariableName)?,
label: decoder.decode(&self.label).to_string(),
mr_type: self.mr_type.clone(),
short_names,
fn parse_counted_string(input: &[u8]) -> Result<(RawString, &[u8]), WarningDetails> {
let Some(space) = input.iter().position(|&b| b == b' ') else {
- return Err(WarningDetails::CountedStringMissingSpace);
+ return Err(MultipleResponseWarning::CountedStringMissingSpace.into());
};
let Ok(length) = from_utf8(&input[..space]) else {
- return Err(WarningDetails::CountedStringInvalidUTF8);
+ return Err(MultipleResponseWarning::CountedStringInvalidUTF8.into());
};
let Ok(length): Result<usize, _> = length.parse() else {
- return Err(WarningDetails::CountedStringInvalidLength(length.into()));
+ return Err(MultipleResponseWarning::CountedStringInvalidLength(length.into()).into());
};
let Some((string, rest)) = input[space + 1..].split_at_checked(length) else {
- return Err(WarningDetails::CountedStringTooLong(length));
+ return Err(MultipleResponseWarning::CountedStringTooLong(length).into());
};
Ok((string.into(), rest))
}
1 => Ok(Some(Measure::Nominal)),
2 => Ok(Some(Measure::Ordinal)),
3 => Ok(Some(Measure::Scale)),
- _ => Err(WarningDetails::InvalidMeasurement(source)),
+ _ => Err(VariableDisplayWarning::InvalidMeasurement(source).into()),
}
}
}
0 => Ok(Some(Alignment::Left)),
1 => Ok(Some(Alignment::Right)),
2 => Ok(Some(Alignment::Center)),
- _ => Err(WarningDetails::InvalidAlignment(source)),
+ _ => Err(VariableDisplayWarning::InvalidAlignment(source).into()),
}
}
}
endian: Endian,
warn: &mut dyn FnMut(Warning),
) -> Result<Record, WarningDetails> {
- if ext.size != 4 {
- return Err(WarningDetails::BadRecordSize {
- record: String::from("variable display record"),
- size: ext.size,
- expected_size: 4,
- });
- }
+ static VAR_DISPLAY_RECORD: ExtensionRecord = ExtensionRecord {
+ size: Some(4),
+ count: None,
+ name: "variable display record",
+ };
+ ext.check_size(&VAR_DISPLAY_RECORD)?;
let n_vars = var_types.n_vars();
let has_width = if ext.count as usize == 3 * n_vars {
} else if ext.count as usize == 2 * n_vars {
false
} else {
- return Err(WarningDetails::InvalidVariableDisplayCount {
+ return Err(VariableDisplayWarning::InvalidVariableDisplayCount {
count: ext.count as usize,
first: 2 * n_vars,
second: 3 * n_vars,
- });
+ }
+ .into());
};
let mut var_displays = Vec::new();
let offset = (ext.data.len() - input.len() - 8) as u64 + ext.offsets.start;
warn(Warning::new(
Some(ext.offsets.clone()),
- WarningDetails::BadLongMissingValueLength { offset, value_len },
+ LongStringMissingValuesWarning::BadValueLength { offset, value_len },
));
read_vec(&mut input, value_len as usize * n_missing_values as usize)?;
continue;
for mv in self.values.iter() {
if let Some(mv) = mv
.decode(decoder)
- .map_err(WarningDetails::InvalidLongStringMissingValueVariableName)
+ .map_err(LongStringMissingValuesWarning::InvalidVariableName)
.issue_warning(&self.offsets, &mut decoder.warn)
{
mvs.push(mv);
input: &'a str,
) -> Result<(Attribute, &'a str), WarningDetails> {
let Some((name, mut input)) = input.split_once('(') else {
- return Err(WarningDetails::AttributeMissingLParen(input.into()));
+ return Err(AttributeWarning::AttributeMissingLParen(input.into()).into());
};
let name = decoder
.new_identifier(name)
- .map_err(WarningDetails::InvalidAttributeName)?;
+ .map_err(AttributeWarning::InvalidAttributeName)?;
let mut values = Vec::new();
loop {
let Some((value, rest)) = input.split_once('\n') else {
- return Err(WarningDetails::AttributeMissingValue {
+ return Err(AttributeWarning::AttributeMissingValue {
name: name.clone(),
index: values.len(),
- });
+ }
+ .into());
};
if let Some(stripped) = value
.strip_prefix('\'')
} else {
decoder.warn(Warning::new(
Some(offsets.clone()),
- WarningDetails::AttributeMissingQuotes {
+ AttributeWarning::AttributeMissingQuotes {
name: name.clone(),
index: values.len(),
},
if !duplicates.is_empty() {
decoder.warn(Warning::new(
Some(self.0.offsets.clone()),
- WarningDetails::DuplicateFileAttributes {
+ AttributeWarning::DuplicateFileAttributes {
attributes: duplicates,
},
));
if !rest.is_empty() {
decoder.warn(Warning::new(
Some(self.0.offsets.clone()),
- WarningDetails::FileAttributesTrailingGarbage,
+ AttributeWarning::FileAttributesTrailingGarbage,
));
}
FileAttributesRecord(set)
input: &'a str,
) -> Result<(VarAttributes, &'a str), WarningDetails> {
let Some((long_var_name, rest)) = input.split_once(':') else {
- return Err(WarningDetails::VariableAttributeMissingColon);
+ return Err(AttributeWarning::VariableAttributeMissingColon.into());
};
let long_var_name = decoder
.new_identifier(long_var_name)
.and_then(Identifier::must_be_ordinary)
- .map_err(WarningDetails::InvalidAttributeVariableName)?;
+ .map_err(AttributeWarning::InvalidAttributeVariableName)?;
let (attributes, rest, duplicates) = Attributes::parse(decoder, offsets, rest, Some('/'))?;
if !duplicates.is_empty() {
decoder.warn(Warning::new(
Some(offsets.clone()),
- WarningDetails::DuplicateVariableAttributes {
+ AttributeWarning::DuplicateVariableAttributes {
variable: long_var_name.clone(),
attributes: duplicates,
},
impl LongName {
fn parse(input: &str, decoder: &Decoder) -> Result<Self, WarningDetails> {
let Some((short_name, long_name)) = input.split_once('=') else {
- return Err(WarningDetails::LongNameMissingEquals);
+ return Err(LongNameWarning::LongNameMissingEquals.into());
};
let short_name = decoder
.new_identifier(short_name)
.and_then(Identifier::must_be_ordinary)
- .map_err(WarningDetails::InvalidShortName)?;
+ .map_err(LongNameWarning::InvalidShortName)?;
let long_name = decoder
.new_identifier(long_name)
.and_then(Identifier::must_be_ordinary)
- .map_err(WarningDetails::InvalidLongName)?;
+ .map_err(LongNameWarning::InvalidLongName)?;
Ok(LongName {
short_name,
long_name,
) -> Result<Self, WarningDetails> {
let (name, input) = input
.split_once('=')
- .ok_or(WarningDetails::VariableSetMissingEquals)?;
+ .ok_or(VariableSetWarning::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(WarningDetails::InvalidVariableSetName)
+ .map_err(VariableSetWarning::InvalidVariableSetName)
.issue_warning(offsets, &mut decoder.warn)
{
vars.push(identifier);
trait IssueWarning<T> {
fn issue_warning(self, offsets: &Range<u64>, warn: &mut dyn FnMut(Warning)) -> Option<T>;
}
-impl<T> IssueWarning<T> for Result<T, WarningDetails> {
+impl<T, W> IssueWarning<T> for Result<T, W>
+where
+ W: Into<WarningDetails>,
+{
fn issue_warning(self, offsets: &Range<u64>, warn: &mut dyn FnMut(Warning)) -> Option<T> {
match self {
Ok(result) => Some(result),
Err(error) => {
- warn(Warning::new(Some(offsets.clone()), error));
+ warn(Warning::new(Some(offsets.clone()), error.into()));
None
}
}
pub fn check_size(&self, expected: &ExtensionRecord) -> Result<(), WarningDetails> {
match expected.size {
Some(expected_size) if self.size != expected_size => {
- return Err(WarningDetails::BadRecordSize {
+ return Err(ExtensionWarning::BadRecordSize {
record: expected.name.into(),
size: self.size,
expected_size,
- });
+ }
+ .into());
}
_ => (),
}
match expected.count {
Some(expected_count) if self.count != expected_count => {
- return Err(WarningDetails::BadRecordCount {
+ return Err(ExtensionWarning::BadRecordCount {
record: expected.name.into(),
count: self.count,
expected_count,
- });
+ }
+ .into());
}
_ => (),
}
Ok(())
}
- pub fn read<R: Read + Seek>(
+ pub(super) fn read<R: Read + Seek>(
r: &mut R,
endian: Endian,
var_types: &VarTypes,
) -> 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(WarningDetails::InvalidLongStringValueLabelName)?;
+ .map_err(LongStringValueLabelWarning::InvalidVariableName)?;
let mut labels = Vec::with_capacity(self.labels.len());
for (value, label) in self.labels.iter() {
if block.uncompressed_size != block_size {
warn(Warning::new(
Some(block_offsets),
- WarningDetails::ZlibTrailerBlockWrongSize {
+ ZlibTrailerWarning::ZlibTrailerBlockWrongSize {
index,
actual: block.uncompressed_size,
expected: block_size,
if block.uncompressed_size > block_size {
warn(Warning::new(
Some(block_offsets),
- WarningDetails::ZlibTrailerBlockTooBig {
+ ZlibTrailerWarning::ZlibTrailerBlockTooBig {
index,
actual: block.uncompressed_size,
max_expected: block_size,
-Warning at file offsets 0xe0 to 0x100: floating point record has bad count 4 instead of the expected 3.
+Warning at file offsets 0xe0 to 0x100: In extension record: floating point record has bad count 4 instead of the expected 3.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0x104: integer record has bad count 9 instead of the expected 8.
+Warning at file offsets 0xe0 to 0x104: In extension record: integer record has bad count 9 instead of the expected 8.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xe5: Missing `=` separator in long variable name record.
+Warning at file offsets 0xe0 to 0xe5: In long variable name record: Missing `=` separator.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0x54 to 0x5c: Compression bias is 50 instead of the usual values of 0 or 100.
+Warning at file offsets 0x54 to 0x5c: In file header: Compression bias is 50 instead of the usual values of 0 or 100.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xfe: Duplicate dataset attributes with names: Attr1.
+Warning at file offsets 0xe0 to 0xfe: In file or variable attribute record: Duplicate dataset attributes with names: Attr1.
-Warning at file offsets 0x10e to 0x12d: Duplicate attributes for variable FIRSTVAR: fred.
+Warning at file offsets 0x10e to 0x12d: In file or variable attribute record: Duplicate attributes for variable FIRSTVAR: fred.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0x140 to 0x1aa: Invalid name in long variable name record. "_Invalid" may not be used as an identifier because it begins with disallowed character '_'.
+Warning at file offsets 0x140 to 0x1aa: In long variable name record: Invalid long name. "_Invalid" may not be used as an identifier because it begins with disallowed character '_'.
-Warning at file offsets 0x140 to 0x1aa: Invalid name in long variable name record. "$Invalid" may not be used as an identifier because it begins with disallowed character '$'.
+Warning at file offsets 0x140 to 0x1aa: In long variable name record: Invalid long name. "$Invalid" may not be used as an identifier because it begins with disallowed character '$'.
-Warning at file offsets 0x140 to 0x1aa: Invalid name in long variable name record. "#Invalid" may not be used as an identifier because it begins with disallowed character '#'.
+Warning at file offsets 0x140 to 0x1aa: In long variable name record: Invalid long name. "#Invalid" may not be used as an identifier because it begins with disallowed character '#'.
Duplicate long variable name LONGVARIABLENAME.
-Warning at file offsets 0x1d8 to 0x21f: Unexpected end of data inside extension record.
+Warning at file offsets 0x1d8 to 0x21f: In extension record: Unexpected end of data.
This system file does not indicate its own character encoding. For best results, specify an encoding explicitly. Use SYSFILE INFO with ENCODING="DETECT" to analyze the possible encodings.
-Warning at file offsets 0x238 to 0x2f2: In long string missing values record, value length at offset 0x2a8 is 12 instead of the expected 8.
+Warning at file offsets 0x238 to 0x2f2: In long string missing values record: Value length at offset 0x2a8 is 12 instead of the expected 8.
File header claims 8 variable positions but 9 were read from file.
-Warning at file offsets 0xe0 to 0xe6: Attribute for Attr1[1] lacks value.
+Warning at file offsets 0xe0 to 0xe6: In file or variable attribute record: Attribute for Attr1[1] lacks value.
-Warning at file offsets 0xf6 to 0x109: Attribute for fred[2] lacks value.
+Warning at file offsets 0xf6 to 0x109: In file or variable attribute record: Attribute for fred[2] lacks value.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xe5: Syntax error parsing counted string (missing trailing space)
+Warning at file offsets 0xe0 to 0xe5: In multiple response set record: Syntax error parsing counted string (missing trailing space).
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xe9: Syntax error parsing counted string (length 4 goes past end of input)
+Warning at file offsets 0xe0 to 0xe9: In multiple response set record: Syntax error parsing counted string (length 4 goes past end of input).
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xe6: Syntax error parsing counted string (missing trailing space)
+Warning at file offsets 0xe0 to 0xe6: In multiple response set record: Syntax error parsing counted string (missing trailing space).
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xe5: Invalid multiple dichotomy label type
+Warning at file offsets 0xe0 to 0xe5: In multiple response set record: Invalid multiple dichotomy label type.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xec: Syntax error in multiple response record (missing variable name delimiter)
+Warning at file offsets 0xe0 to 0xec: In multiple response set record: Syntax error (missing variable name delimiter).
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xe5: Syntax error in multiple response record (missing space after multiple response type)
+Warning at file offsets 0xe0 to 0xe5: In multiple response set record: Syntax error (missing space after multiple response type).
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xea: Syntax error in multiple response record (missing space after multiple response type)
+Warning at file offsets 0xe0 to 0xea: In multiple response set record: Syntax error (missing space after multiple response type).
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xe5: Invalid multiple dichotomy label type
+Warning at file offsets 0xe0 to 0xe5: In multiple response set record: Invalid multiple dichotomy label type.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xe6: Invalid multiple dichotomy label type
+Warning at file offsets 0xe0 to 0xe6: In multiple response set record: Invalid multiple dichotomy label type.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0x1c8 to 0x1e8: variable attributes record has bad size 4 bytes instead of the expected 1.
+Warning at file offsets 0x1c8 to 0x1e8: In extension record: variable attributes record has bad size 4 bytes instead of the expected 1.
This system file does not indicate its own character encoding. For best results, specify an encoding explicitly. Use SYSFILE INFO with ENCODING="DETECT" to analyze the possible encodings.
-Warning at file offsets 0xe0 to 0xed: Attribute for Attr1[1] missing quotations.
+Warning at file offsets 0xe0 to 0xed: In file or variable attribute record: Attribute for Attr1[1] missing quotations.
-Warning at file offsets 0xfd to 0x10f: Attribute for fred[1] missing quotations.
+Warning at file offsets 0xfd to 0x10f: In file or variable attribute record: Attribute for fred[1] missing quotations.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0x110 to 0x118: One or more variable indexes for value labels were not in the valid range [1,2] or referred to string continuations: [3, 4]
+Warning at file offsets 0x110 to 0x118: In value label record: One or more variable indexes were not in the valid range [1,2] or referred to string continuations: [3, 4]
-Warning at file offsets 0x138 to 0x140: One or more variable indexes for value labels were not in the valid range [1,2] or referred to string continuations: [5, 6]
+Warning at file offsets 0x138 to 0x140: In value label record: One or more variable indexes were not in the valid range [1,2] or referred to string continuations: [5, 6]
-Warning at file offsets 0x160 to 0x168: One or more variable indexes for value labels were not in the valid range [1,2] or referred to string continuations: [7, 8]
+Warning at file offsets 0x160 to 0x168: In value label record: One or more variable indexes were not in the valid range [1,2] or referred to string continuations: [7, 8]
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0x110 to 0x114: One or more variable indexes for value labels were not in the valid range [1,2] or referred to string continuations: [2]
+Warning at file offsets 0x110 to 0x114: In value label record: One or more variable indexes were not in the valid range [1,2] or referred to string continuations: [2]
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xec to 0xf0: At least one valid variable index for value labels is required but none were specified.
+Warning at file offsets 0xec to 0xf0: In value label record: At least one valid variable index is required but none were specified.
This system file does not indicate its own character encoding. For best results, specify an encoding explicitly. Use SYSFILE INFO with ENCODING="DETECT" to analyze the possible encodings.
-Warning at file offsets 0x110 to 0x118: The first variable index is for a string variable but the following variable indexes are for numeric variables: [2]
+Warning at file offsets 0x110 to 0x118: In value label record: First variable index is for a string variable but the following variable indexes are for numeric variables: [2]
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xe8: Invalid variable display alignment value 4294967295
+Warning at file offsets 0xe0 to 0xe8: In variable display record: Invalid variable display alignment value 4294967295.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xe8: Invalid variable measurement level value 4
+Warning at file offsets 0xe0 to 0xe8: In variable display record: Invalid variable measurement level value 4.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xf0: Variable display record contains 4 items but should contain either 2 or 3.
+Warning at file offsets 0xe0 to 0xf0: In variable display record: Record contains 4 items but should contain either 2 or 3.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0xe0 to 0xf0: variable display record has bad size 8 bytes instead of the expected 4.
+Warning at file offsets 0xe0 to 0xf0: In extension record: variable display record has bad size 8 bytes instead of the expected 4.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│
-Warning at file offsets 0x1c4 to 0x1dc: ZLIB block descriptor 0 reported block size 0x400000, when at most 0x3ff000 was expected.
+Warning at file offsets 0x1c4 to 0x1dc: In ZLIB trailer: ZLIB block descriptor 0 reported block size 0x400000, when at most 0x3ff000 was expected.
╭──────────────────────┬────────────────────────╮
│ Created │ 01-JAN-2011 20:53:52│