+pub fn decode(
+ headers: Vec<raw::Record>,
+ encoding: Option<&'static Encoding>,
+ warn: &impl Fn(Error),
+) -> Result<Vec<Record>, Error> {
+ let Some(header_record) = headers.iter().find_map(|rec| {
+ if let raw::Record::Header(header) = rec {
+ Some(header)
+ } else {
+ None
+ }
+ }) else {
+ return Err(Error::MissingHeaderRecord);
+ };
+ let encoding = match encoding {
+ Some(encoding) => encoding,
+ None => {
+ let encoding = headers.iter().find_map(|rec| {
+ if let raw::Record::Encoding(ref e) = rec {
+ Some(e.0.as_str())
+ } else {
+ None
+ }
+ });
+ let character_code = headers.iter().find_map(|rec| {
+ if let raw::Record::IntegerInfo(ref r) = rec {
+ Some(r.character_code)
+ } else {
+ None
+ }
+ });
+ match get_encoding(encoding, character_code) {
+ Ok(encoding) => encoding,
+ Err(err @ EncodingError::Ebcdic) => return Err(Error::EncodingError(err)),
+ Err(err) => {
+ warn(Error::EncodingError(err));
+ // Warn that we're using the default encoding.
+ default_encoding()
+ }
+ }
+ }
+ };
+
+ let mut decoder = Decoder {
+ compression: header_record.compression,
+ endian: header_record.endian,
+ encoding,
+ variables: HashMap::new(),
+ var_names: HashMap::new(),
+ n_dict_indexes: 0,
+ n_generated_names: 0,
+ };
+
+ let mut output = Vec::with_capacity(headers.len());
+ for header in &headers {
+ match header {
+ raw::Record::Header(ref input) => {
+ if let Some(header) = HeaderRecord::try_decode(&mut decoder, input, warn)? {
+ output.push(Record::Header(header))
+ }
+ }
+ raw::Record::Variable(ref input) => {
+ if let Some(variable) = VariableRecord::try_decode(&mut decoder, input, warn)? {
+ output.push(Record::Variable(variable));
+ }
+ }
+ raw::Record::ValueLabel(ref input) => {
+ if let Some(value_label) = ValueLabelRecord::try_decode(&mut decoder, input, warn)?
+ {
+ output.push(Record::ValueLabel(value_label));
+ }
+ }
+ raw::Record::Document(ref input) => {
+ if let Some(document) = DocumentRecord::try_decode(&mut decoder, input, warn)? {
+ output.push(Record::Document(document))
+ }
+ }
+ raw::Record::IntegerInfo(ref input) => output.push(Record::IntegerInfo(input.clone())),
+ raw::Record::FloatInfo(ref input) => output.push(Record::FloatInfo(input.clone())),
+ raw::Record::VariableSets(ref input) => {
+ let s = decoder.decode_string_cow(&input.text.0, warn);
+ output.push(Record::VariableSets(VariableSetRecord::parse(&s, warn)?));
+ }
+ raw::Record::VarDisplay(ref input) => {
+ if let Some(vdr) = VarDisplayRecord::try_decode(&mut decoder, input, warn)? {
+ output.push(Record::VarDisplay(vdr))
+ }
+ }
+ raw::Record::MultipleResponse(ref input) => {
+ if let Some(mrr) = MultipleResponseRecord::try_decode(&mut decoder, input, warn)? {
+ output.push(Record::MultipleResponse(mrr))
+ }
+ }
+ raw::Record::LongStringMissingValues(ref input) => {
+ if let Some(mrr) = LongStringMissingValuesRecord::try_decode(&mut decoder, input, warn)? {
+ output.push(Record::LongStringMissingValues(mrr))
+ }
+ }
+ raw::Record::LongStringValueLabels(ref input) => {
+ if let Some(mrr) =
+ LongStringValueLabelRecord::try_decode(&mut decoder, input, warn)?
+ {
+ output.push(Record::LongStringValueLabels(mrr))
+ }
+ }
+ raw::Record::Encoding(ref input) => output.push(Record::Encoding(input.clone())),
+ raw::Record::NumberOfCases(ref input) => {
+ output.push(Record::NumberOfCases(input.clone()))
+ }
+ raw::Record::ProductInfo(ref input) => {
+ let s = decoder.decode_string_cow(&input.text.0, warn);
+ output.push(Record::ProductInfo(ProductInfoRecord::parse(&s, warn)?));
+ }
+ raw::Record::LongNames(ref input) => {
+ let s = decoder.decode_string_cow(&input.text.0, warn);
+ output.push(Record::LongNames(LongNameRecord::parse(
+ &mut decoder,
+ &s,
+ warn,
+ )?));
+ }
+ raw::Record::VeryLongStrings(ref input) => {
+ let s = decoder.decode_string_cow(&input.text.0, warn);
+ output.push(Record::VeryLongStrings(VeryLongStringRecord::parse(
+ &decoder, &s, warn,
+ )?));
+ }
+ raw::Record::FileAttributes(ref input) => {
+ let s = decoder.decode_string_cow(&input.text.0, warn);
+ output.push(Record::FileAttributes(FileAttributeRecord::parse(
+ &decoder, &s, warn,
+ )?));
+ }
+ raw::Record::VariableAttributes(ref input) => {
+ let s = decoder.decode_string_cow(&input.text.0, warn);
+ output.push(Record::VariableAttributes(VariableAttributeRecord::parse(
+ &decoder, &s, warn,
+ )?));
+ }
+ raw::Record::OtherExtension(ref input) => {
+ output.push(Record::OtherExtension(input.clone()))
+ }
+ raw::Record::EndOfHeaders(_) => (),
+ raw::Record::ZHeader(_) => (),
+ raw::Record::ZTrailer(_) => (),
+ raw::Record::Case(_) => (),
+ };
+ }
+ Ok(output)
+}
+