}
*/
-trait ExtensionRecord where Self: Sized {
+trait ExtensionRecord
+where
+ Self: Sized,
+{
const SIZE: Option<u32>;
const COUNT: Option<u32>;
const NAME: &'static str;
- fn parse(ext: &Extension, endian: Endian) -> Result<Self, Error>;
+ fn parse(ext: &Extension, endian: Endian, warn: impl Fn(Error)) -> Result<Self, Error>;
}
pub struct IntegerInfo {
const COUNT: Option<u32> = Some(8);
const NAME: &'static str = "integer record";
- fn parse(ext: &Extension, endian: Endian) -> Result<Self, Error>{
+ fn parse(ext: &Extension, endian: Endian, _warn: impl Fn(Error)) -> Result<Self, Error> {
ext.check_size::<Self>()?;
let mut input = &ext.data[..];
floating_point_rep: data[4],
compression_code: data[5],
endianness: data[6],
- character_code: data[7]
+ character_code: data[7],
})
}
}
const COUNT: Option<u32> = Some(3);
const NAME: &'static str = "floating point record";
- fn parse(ext: &Extension, endian: Endian) -> Result<Self, Error>{
+ fn parse(ext: &Extension, endian: Endian, _warn: impl Fn(Error)) -> Result<Self, Error> {
ext.check_size::<Self>()?;
let mut input = &ext.data[..];
}
}
+pub struct VarDisplayRecord(Vec<u32>);
+
+impl ExtensionRecord for VarDisplayRecord {
+ const SIZE: Option<u32> = Some(4);
+ const COUNT: Option<u32> = None;
+ const NAME: &'static str = "variable display record";
+
+ fn parse(ext: &Extension, endian: Endian, _warn: impl Fn(Error)) -> Result<Self, Error> {
+ ext.check_size::<Self>()?;
+
+ let mut input = &ext.data[..];
+ let display = (0..ext.count)
+ .map(|_| endian.parse(read_bytes(&mut input).unwrap()))
+ .collect();
+ Ok(VarDisplayRecord(display))
+ }
+}
+
+pub struct LongStringValueLabels {
+ pub var_name: Vec<u8>,
+ pub width: u32,
+
+ /// `(value, label)` pairs, where each value is `width` bytes.
+ pub labels: Vec<(Vec<u8>, Vec<u8>)>,
+}
+
+pub struct LongStringValueLabelSet(Vec<LongStringValueLabels>);
+
+impl ExtensionRecord for LongStringValueLabelSet {
+ const SIZE: Option<u32> = Some(1);
+ const COUNT: Option<u32> = None;
+ const NAME: &'static str = "long string value labels record";
+
+ fn parse(ext: &Extension, endian: Endian, _warn: impl Fn(Error)) -> Result<Self, Error> {
+ ext.check_size::<Self>()?;
+
+ let mut input = &ext.data[..];
+ let mut label_set = Vec::new();
+ while !input.is_empty() {
+ let var_name = read_string(&mut input, endian)?;
+ let width: u32 = endian.parse(read_bytes(&mut input)?);
+ let n_labels: u32 = endian.parse(read_bytes(&mut input)?);
+ let mut labels = Vec::new();
+ for _ in 0..n_labels {
+ let value = read_string(&mut input, endian)?;
+ let label = read_string(&mut input, endian)?;
+ labels.push((value, label));
+ }
+ label_set.push(LongStringValueLabels {
+ var_name,
+ width,
+ labels,
+ })
+ }
+ Ok(LongStringValueLabelSet(label_set))
+ }
+}
+
+pub struct LongStringMissingValues {
+ /// Variable name.
+ pub var_name: Vec<u8>,
+
+ /// Up to three missing values.
+ pub missing_values: Vec<[u8; 8]>,
+}
+
+pub struct LongStringMissingValueSet(Vec<LongStringMissingValues>);
+
+impl ExtensionRecord for LongStringMissingValueSet {
+ const SIZE: Option<u32> = Some(1);
+ const COUNT: Option<u32> = None;
+ const NAME: &'static str = "long string missing values record";
+
+ fn parse(ext: &Extension, endian: Endian, _warn: impl Fn(Error)) -> Result<Self, Error> {
+ ext.check_size::<Self>()?;
+
+ let mut input = &ext.data[..];
+ let mut missing_value_set = Vec::new();
+ while !input.is_empty() {
+ let var_name = read_string(&mut input, endian)?;
+ let n_missing_values: u8 = endian.parse(read_bytes(&mut input)?);
+ 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.offset;
+ return Err(Error::BadLongMissingValueLength {
+ record_offset: ext.offset,
+ offset,
+ value_len,
+ });
+ }
+ let mut missing_values = Vec::new();
+ for i in 0..n_missing_values {
+ let value: [u8; 8] = read_bytes(&mut input)?;
+ let numeric_value: u64 = endian.parse(value);
+ let value = if i > 0 && numeric_value == 8 {
+ // Tolerate files written by old, buggy versions of PSPP
+ // where we believed that the value_length was repeated
+ // before each missing value.
+ read_bytes(&mut input)?
+ } else {
+ value
+ };
+ missing_values.push(value);
+ }
+ missing_value_set.push(LongStringMissingValues {
+ var_name,
+ missing_values,
+ });
+ }
+ Ok(LongStringMissingValueSet(missing_value_set))
+ }
+}
+
pub struct Encoding(pub String);
impl ExtensionRecord for Encoding {
const COUNT: Option<u32> = None;
const NAME: &'static str = "encoding record";
- fn parse(ext: &Extension, endian: Endian) -> Result<Self, Error>{
+ fn parse(ext: &Extension, _endian: Endian, _warn: impl Fn(Error)) -> Result<Self, Error> {
+ ext.check_size::<Self>()?;
+
+ Ok(Encoding(String::from_utf8(ext.data.clone()).map_err(
+ |_| Error::BadEncodingName { offset: ext.offset },
+ )?))
+ }
+}
+
+pub struct NumberOfCasesRecord {
+ /// Always observed as 1.
+ one: u64,
+
+ /// Number of cases.
+ n_cases: u64,
+}
+
+impl ExtensionRecord for NumberOfCasesRecord {
+ const SIZE: Option<u32> = Some(8);
+ const COUNT: Option<u32> = Some(2);
+ const NAME: &'static str = "extended number of cases record";
+
+ fn parse(ext: &Extension, endian: Endian, _warn: impl Fn(Error)) -> Result<Self, Error> {
ext.check_size::<Self>()?;
- Ok(Encoding(String::from_utf8(ext.data)?))
+ let mut input = &ext.data[..];
+ let one = endian.parse(read_bytes(&mut input)?);
+ let n_cases = endian.parse(read_bytes(&mut input)?);
+
+ Ok(NumberOfCasesRecord { one, n_cases })
}
}
r.read_exact(&mut vec)?;
Ok(vec)
}
+
+fn read_string<R: Read>(r: &mut R, endian: Endian) -> Result<Vec<u8>, IoError> {
+ let length: u32 = endian.parse(read_bytes(r)?);
+ read_vec(r, length as usize)
+}