1 use std::{borrow::Cow, cmp::Ordering, collections::HashMap, iter::repeat};
5 format::{Error as FormatError, Spec, UncheckedSpec},
6 identifier::{Error as IdError, Identifier},
7 raw::{self, MissingValues, VarType},
9 use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
10 use encoding_rs::{DecoderResult, Encoding};
11 use num::integer::div_ceil;
12 use ordered_float::OrderedFloat;
13 use thiserror::Error as ThisError;
15 pub use crate::raw::{CategoryLabels, Compression};
17 #[derive(ThisError, Debug)]
19 #[error("Variable record at offset {offset:#x} specifies width {width} not in valid range [-1,255).")]
20 InvalidVariableWidth { offset: u64, width: i32 },
22 #[error("This file has corrupted metadata written by a buggy version of PSPP. To ensure that other software can read it correctly, save a new copy of the file.")]
23 InvalidLongMissingValueFormat,
25 #[error("File creation date {creation_date} is not in the expected format \"DD MMM YY\" format. Using 01 Jan 1970.")]
26 InvalidCreationDate { creation_date: String },
28 #[error("File creation time {creation_time} is not in the expected format \"HH:MM:SS\" format. Using midnight.")]
29 InvalidCreationTime { creation_time: String },
31 #[error("{id_error} Renaming variable to {new_name}.")]
38 "Substituting {new_spec} for invalid print format on variable {variable}. {format_error}"
43 format_error: FormatError,
47 "Substituting {new_spec} for invalid write format on variable {variable}. {format_error}"
52 format_error: FormatError,
55 #[error("Renaming variable with duplicate name {duplicate_name} to {new_name}.")]
56 DuplicateVariableName {
57 duplicate_name: Identifier,
61 #[error("Dictionary index {dict_index} is outside valid range [1,{max_index}].")]
62 InvalidDictIndex { dict_index: usize, max_index: usize },
64 #[error("Dictionary index {0} refers to a long string continuation.")]
65 DictIndexIsContinuation(usize),
67 #[error("Variables associated with value label are not all of identical type. Variable {numeric_var} is numeric, but variable {string_var} is string.")]
68 ValueLabelsDifferentTypes {
69 numeric_var: Identifier,
70 string_var: Identifier,
74 "Value labels may not be added to long string variable {0} using record types 3 or 4."
76 InvalidLongStringValueLabel(Identifier),
78 #[error("Invalid multiple response set name. {0}")]
79 InvalidMrSetName(#[from] IdError),
81 #[error("Multiple response set {mr_set} includes unknown variable {short_name}.")]
82 UnknownMrSetVariable {
84 short_name: Identifier,
87 #[error("Multiple response set {0} has no variables.")]
88 EmptyMrSet(Identifier),
90 #[error("Multiple response set {0} has only one variable.")]
91 OneVarMrSet(Identifier),
93 #[error("Multiple response set {0} contains both string and numeric variables.")]
94 MixedMrSet(Identifier),
96 #[error("Invalid numeric format for counted value {number} in multiple response set {mr_set}.")]
97 InvalidMDGroupCountedValue { mr_set: Identifier, number: String },
99 #[error("Counted value {value} has width {width}, but it must be no wider than {max_width}, the width of the narrowest variable in multiple response set {mr_set}.")]
100 TooWideMDGroupCountedValue { mr_set: Identifier, value: String, width: usize, max_width: u16 },
102 #[error("Details TBD")]
106 #[derive(Clone, Debug)]
108 Header(HeaderRecord),
109 Variable(VariableRecord),
110 ValueLabel(ValueLabelRecord),
111 Document(DocumentRecord),
112 IntegerInfo(IntegerInfoRecord),
113 FloatInfo(FloatInfoRecord),
114 VariableSets(VariableSetRecord),
115 VarDisplay(VarDisplayRecord),
116 MultipleResponse(MultipleResponseRecord),
117 //LongStringValueLabels(LongStringValueLabelRecord),
118 Encoding(EncodingRecord),
119 NumberOfCases(NumberOfCasesRecord),
120 ProductInfo(ProductInfoRecord),
121 //LongNames(UnencodedString),
122 //LongStrings(UnencodedString),
123 //FileAttributes(UnencodedString),
124 //VariableAttributes(UnencodedString),
125 //OtherExtension(Extension),
128 //ZTrailer(ZTrailer),
132 pub use crate::raw::EncodingRecord;
133 pub use crate::raw::FloatInfoRecord;
134 pub use crate::raw::IntegerInfoRecord;
135 pub use crate::raw::NumberOfCasesRecord;
137 type DictIndex = usize;
139 pub struct Variable {
140 pub dict_index: DictIndex,
141 pub short_name: Identifier,
142 pub long_name: Option<Identifier>,
147 pub compression: Option<Compression>,
149 pub encoding: &'static Encoding,
150 pub variables: HashMap<DictIndex, Variable>,
151 pub var_names: HashMap<Identifier, DictIndex>,
152 n_dict_indexes: usize,
153 n_generated_names: usize,
157 fn generate_name(&mut self) -> Identifier {
159 self.n_generated_names += 1;
160 let name = Identifier::new(&format!("VAR{:03}", self.n_generated_names), self.encoding)
162 if !self.var_names.contains_key(&name) {
165 assert!(self.n_generated_names < usize::MAX);
168 fn decode_string<'a>(&self, input: &'a [u8], warn: &impl Fn(Error)) -> Cow<'a, str> {
169 let (output, malformed) = self.encoding.decode_without_bom_handling(input);
175 pub fn decode_identifier(
178 warn: &impl Fn(Error),
179 ) -> Result<Identifier, IdError> {
180 let s = self.decode_string(input, warn);
181 Identifier::new(&s, self.encoding)
183 fn get_var_by_index(&self, dict_index: usize) -> Result<&Variable, Error> {
184 let max_index = self.n_dict_indexes - 1;
185 if dict_index == 0 || dict_index as usize > max_index {
186 return Err(Error::InvalidDictIndex {
191 let Some(variable) = self.variables.get(&dict_index) else {
192 return Err(Error::DictIndexIsContinuation(dict_index));
197 /// Returns `input` decoded from `self.encoding` into UTF-8 such that
198 /// re-encoding the result back into `self.encoding` will have exactly the
199 /// same length in bytes.
201 /// XXX warn about errors?
202 fn decode_exact_length<'a>(&self, input: &'a [u8]) -> Cow<'a, str> {
203 if let (s, false) = self.encoding.decode_without_bom_handling(input) {
204 // This is the common case. Usually there will be no errors.
207 // Unusual case. Don't bother to optimize it much.
208 let mut decoder = self.encoding.new_decoder_without_bom_handling();
209 let mut output = String::with_capacity(
211 .max_utf8_buffer_length_without_replacement(input.len())
214 let mut rest = input;
215 while !rest.is_empty() {
216 match decoder.decode_to_string_without_replacement(rest, &mut output, true) {
217 (DecoderResult::InputEmpty, _) => break,
218 (DecoderResult::OutputFull, _) => unreachable!(),
219 (DecoderResult::Malformed(a, b), consumed) => {
220 let skipped = a as usize + b as usize;
221 output.extend(repeat('?').take(skipped));
222 rest = &rest[consumed..];
226 assert_eq!(self.encoding.encode(&output).0.len(), input.len());
232 pub trait Decode: Sized {
234 fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error>;
237 #[derive(Clone, Debug)]
238 pub struct HeaderRecord {
239 pub eye_catcher: String,
240 pub weight_index: Option<usize>,
241 pub n_cases: Option<u64>,
242 pub creation: NaiveDateTime,
243 pub file_label: String,
246 impl Decode for HeaderRecord {
247 type Input = crate::raw::HeaderRecord;
249 fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error> {
250 let eye_catcher = decoder.decode_string(&input.eye_catcher.0, &warn);
251 let file_label = decoder.decode_string(&input.file_label.0, &warn);
252 let creation_date = decoder.decode_string(&input.creation_date.0, &warn);
253 let creation_date = NaiveDate::parse_from_str(&creation_date, "%v").unwrap_or_else(|_| {
254 warn(Error::InvalidCreationDate {
255 creation_date: creation_date.into(),
259 let creation_time = decoder.decode_string(&input.creation_time.0, &warn);
261 NaiveTime::parse_from_str(&creation_time, "%H:%M:%S").unwrap_or_else(|_| {
262 warn(Error::InvalidCreationTime {
263 creation_time: creation_time.into(),
268 eye_catcher: eye_catcher.into(),
269 weight_index: input.weight_index.map(|n| n as usize),
270 n_cases: input.n_cases.map(|n| n as u64),
271 creation: NaiveDateTime::new(creation_date, creation_time),
272 file_label: file_label.into(),
277 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
283 impl PartialOrd for VarWidth {
284 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
285 match (self, other) {
286 (VarWidth::Numeric, VarWidth::Numeric) => Some(Ordering::Equal),
287 (VarWidth::String(a), VarWidth::String(b)) => Some(a.cmp(b)),
294 fn n_dict_indexes(self) -> usize {
296 VarWidth::Numeric => 1,
297 VarWidth::String(w) => div_ceil(w as usize, 8),
304 f: impl Fn(u16, u16) -> u16,
305 ) -> Option<VarWidth> {
307 (Some(VarWidth::Numeric), Some(VarWidth::Numeric)) => Some(VarWidth::Numeric),
308 (Some(VarWidth::String(a)), Some(VarWidth::String(b))) => {
309 Some(VarWidth::String(f(a, b)))
315 /// Returns the wider of `self` and `other`:
316 /// - Numerical variable widths are equally wide.
317 /// - Longer strings are wider than shorter strings.
318 /// - Numerical and string types are incomparable, so result in `None`.
319 /// - Any `None` in the input yields `None` in the output.
320 pub fn wider(a: Option<VarWidth>, b: Option<VarWidth>) -> Option<VarWidth> {
321 Self::width_predicate(a, b, |a, b| a.max(b))
324 /// Returns the narrower of `self` and `other` (see [`Self::wider`]).
325 pub fn narrower(a: Option<VarWidth>, b: Option<VarWidth>) -> Option<VarWidth> {
326 Self::width_predicate(a, b, |a, b| a.min(b))
330 impl From<VarWidth> for VarType {
331 fn from(source: VarWidth) -> Self {
333 VarWidth::Numeric => VarType::Numeric,
334 VarWidth::String(_) => VarType::String,
339 #[derive(Clone, Debug)]
340 pub struct VariableRecord {
342 pub name: Identifier,
343 pub print_format: Spec,
344 pub write_format: Spec,
345 pub missing_values: MissingValues,
346 pub label: Option<String>,
349 fn decode_format(raw: raw::Spec, width: VarWidth, warn: impl Fn(Spec, FormatError)) -> Spec {
350 UncheckedSpec::try_from(raw)
351 .and_then(Spec::try_from)
352 .and_then(|x| x.check_width_compatibility(width))
353 .unwrap_or_else(|error| {
354 let new_format = Spec::default_for_width(width);
355 warn(new_format, error);
360 impl VariableRecord {
362 decoder: &mut Decoder,
363 input: &crate::raw::VariableRecord,
364 warn: impl Fn(Error),
365 ) -> Result<Option<VariableRecord>, Error> {
366 let width = match input.width {
367 0 => VarWidth::Numeric,
368 w @ 1..=255 => VarWidth::String(w as u16),
369 -1 => return Ok(None),
371 return Err(Error::InvalidVariableWidth {
372 offset: input.offset,
377 let name = match decoder.decode_identifier(&input.name.0, &warn) {
379 if !decoder.var_names.contains_key(&name) {
382 let new_name = decoder.generate_name();
383 warn(Error::DuplicateVariableName {
384 duplicate_name: name.clone(),
385 new_name: new_name.clone(),
391 let new_name = decoder.generate_name();
392 warn(Error::InvalidVariableName {
394 new_name: new_name.clone(),
399 let variable = Variable {
400 dict_index: decoder.n_dict_indexes,
401 short_name: name.clone(),
405 decoder.n_dict_indexes += width.n_dict_indexes();
408 .insert(name.clone(), variable.dict_index)
412 .insert(variable.dict_index, variable)
415 let print_format = decode_format(input.print_format, width, |new_spec, format_error| {
416 warn(Error::InvalidPrintFormat {
418 variable: name.clone(),
422 let write_format = decode_format(input.write_format, width, |new_spec, format_error| {
423 warn(Error::InvalidWriteFormat {
425 variable: name.clone(),
432 .map(|label| decoder.decode_string(&label.0, &warn).into());
433 Ok(Some(VariableRecord {
438 missing_values: input.missing_values.clone(),
444 #[derive(Clone, Debug)]
445 pub struct DocumentRecord(Vec<String>);
447 impl Decode for DocumentRecord {
448 type Input = crate::raw::DocumentRecord;
450 fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error> {
455 .map(|s| decoder.decode_string(&s.0, &warn).into())
465 const NAME: &'static str;
466 fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error>;
469 #[derive(Clone, Debug)]
470 pub struct VariableSet {
472 pub vars: Vec<String>,
476 fn parse(input: &str) -> Result<Self, Error> {
477 let (name, input) = input.split_once('=').ok_or(Error::TBD)?;
478 let vars = input.split_ascii_whitespace().map(String::from).collect();
486 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
488 Number(Option<OrderedFloat<f64>>),
493 pub fn decode(raw: raw::Value, decoder: &Decoder) -> Self {
495 raw::Value::Number(x) => Value::Number(x.map(|x| x.into())),
496 raw::Value::String(s) => Value::String(decoder.decode_exact_length(&s.0).into()),
501 #[derive(Clone, Debug)]
502 pub struct ValueLabelRecord {
503 pub var_type: VarType,
504 pub labels: Vec<(Value, String)>,
505 pub variables: Vec<Identifier>,
508 trait WarnOnError<T> {
509 fn warn_on_error<F: Fn(Error)>(self, warn: &F) -> Option<T>;
511 impl<T> WarnOnError<T> for Result<T, Error> {
512 fn warn_on_error<F: Fn(Error)>(self, warn: &F) -> Option<T> {
514 Ok(result) => Some(result),
523 impl ValueLabelRecord {
525 decoder: &mut Decoder,
526 raw_value_label: &crate::raw::ValueLabelRecord,
527 dict_indexes: &crate::raw::VarIndexRecord,
528 warn: impl Fn(Error),
529 ) -> Result<Option<ValueLabelRecord>, Error> {
530 let variables: Vec<&Variable> = dict_indexes
533 .filter_map(|&dict_index| {
535 .get_var_by_index(dict_index as usize)
536 .warn_on_error(&warn)
538 .filter(|&variable| match variable.width {
539 VarWidth::String(width) if width > 8 => {
540 warn(Error::InvalidLongStringValueLabel(
541 variable.short_name.clone(),
548 let mut i = variables.iter();
549 let Some(&first_var) = i.next() else {
552 let var_type: VarType = first_var.width.into();
554 let this_type: VarType = variable.width.into();
555 if var_type != this_type {
556 let (numeric_var, string_var) = match var_type {
557 VarType::Numeric => (first_var, variable),
558 VarType::String => (variable, first_var),
560 warn(Error::ValueLabelsDifferentTypes {
561 numeric_var: numeric_var.short_name.clone(),
562 string_var: string_var.short_name.clone(),
567 let labels = raw_value_label
570 .map(|(value, label)| {
571 let label = decoder.decode_string(&label.0, &warn);
572 let value = Value::decode(
573 raw::Value::from_raw(*value, var_type, decoder.endian),
576 (value, label.into())
579 let variables = variables
581 .map(|&variable| variable.short_name.clone())
583 Ok(Some(ValueLabelRecord {
591 #[derive(Clone, Debug)]
592 pub struct VariableSetRecord(Vec<VariableSet>);
594 impl TextRecord for VariableSetRecord {
595 const NAME: &'static str = "variable set";
596 fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
597 let mut sets = Vec::new();
598 for line in input.lines() {
599 if let Some(set) = VariableSet::parse(line).warn_on_error(&warn) {
603 Ok(VariableSetRecord(sets))
607 #[derive(Clone, Debug)]
608 pub struct ProductInfoRecord(pub String);
610 impl TextRecord for ProductInfoRecord {
611 const NAME: &'static str = "extra product info";
612 fn parse(input: &str, _warn: impl Fn(Error)) -> Result<Self, Error> {
613 Ok(ProductInfoRecord(input.into()))
617 pub struct LongVariableName {
618 pub short_name: String,
619 pub long_name: String,
622 pub struct LongVariableNameRecord(Vec<LongVariableName>);
624 impl TextRecord for LongVariableNameRecord {
625 const NAME: &'static str = "long variable names";
626 fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
627 let mut names = Vec::new();
628 for pair in input.split('\t').filter(|s| !s.is_empty()) {
629 if let Some((short_name, long_name)) = pair.split_once('=') {
630 let name = LongVariableName {
631 short_name: short_name.into(),
632 long_name: long_name.into(),
639 Ok(LongVariableNameRecord(names))
643 pub struct VeryLongString {
644 pub short_name: String,
648 impl VeryLongString {
649 fn parse(input: &str) -> Result<VeryLongString, Error> {
650 let Some((short_name, length)) = input.split_once('=') else {
651 return Err(Error::TBD);
653 let length: usize = length.parse().map_err(|_| Error::TBD)?;
655 short_name: short_name.into(),
661 pub struct VeryLongStringRecord(Vec<VeryLongString>);
663 impl TextRecord for VeryLongStringRecord {
664 const NAME: &'static str = "very long strings";
665 fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
666 let mut very_long_strings = Vec::new();
669 .map(|s| s.trim_end_matches('\t'))
670 .filter(|s| !s.is_empty())
672 if let Some(vls) = VeryLongString::parse(tuple).warn_on_error(&warn) {
673 very_long_strings.push(vls)
676 Ok(VeryLongStringRecord(very_long_strings))
680 pub struct Attribute {
682 pub values: Vec<String>,
686 fn parse<'a>(input: &'a str, warn: &impl Fn(Error)) -> Result<(Attribute, &'a str), Error> {
687 let Some((name, mut input)) = input.split_once('(') else {
688 return Err(Error::TBD);
690 let mut values = Vec::new();
692 let Some((value, rest)) = input.split_once('\n') else {
693 return Err(Error::TBD);
695 if let Some(stripped) = value
697 .and_then(|value| value.strip_suffix('\''))
699 values.push(stripped.into());
702 values.push(value.into());
704 if let Some(rest) = rest.strip_prefix(')') {
718 pub struct AttributeSet(pub Vec<Attribute>);
723 sentinel: Option<char>,
724 warn: &impl Fn(Error),
725 ) -> Result<(AttributeSet, &'a str), Error> {
726 let mut attributes = Vec::new();
728 match input.chars().next() {
730 c if c == sentinel => break &input[1..],
732 let (attribute, rest) = Attribute::parse(input, &warn)?;
733 attributes.push(attribute);
738 Ok((AttributeSet(attributes), rest))
742 pub struct FileAttributeRecord(AttributeSet);
744 impl TextRecord for FileAttributeRecord {
745 const NAME: &'static str = "data file attributes";
746 fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
747 let (set, rest) = AttributeSet::parse(input, None, &warn)?;
748 if !rest.is_empty() {
751 Ok(FileAttributeRecord(set))
755 pub struct VarAttributeSet {
756 pub long_var_name: String,
757 pub attributes: AttributeSet,
760 impl VarAttributeSet {
763 warn: &impl Fn(Error),
764 ) -> Result<(VarAttributeSet, &'a str), Error> {
765 let Some((long_var_name, rest)) = input.split_once(':') else {
766 return Err(Error::TBD);
768 let (attributes, rest) = AttributeSet::parse(rest, Some('/'), warn)?;
771 long_var_name: long_var_name.into(),
779 pub struct VariableAttributeRecord(Vec<VarAttributeSet>);
781 impl TextRecord for VariableAttributeRecord {
782 const NAME: &'static str = "variable attributes";
783 fn parse(mut input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
784 let mut var_attribute_sets = Vec::new();
785 while !input.is_empty() {
786 let Some((var_attribute, rest)) =
787 VarAttributeSet::parse(input, &warn).warn_on_error(&warn)
791 var_attribute_sets.push(var_attribute);
794 Ok(VariableAttributeRecord(var_attribute_sets))
798 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
805 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
812 #[derive(Clone, Debug)]
813 pub struct VarDisplay {
814 pub measure: Option<Measure>,
816 pub align: Option<Alignment>,
819 #[derive(Clone, Debug)]
820 pub struct VarDisplayRecord(pub Vec<VarDisplay>);
822 #[derive(Clone, Debug)]
823 pub enum MultipleResponseType {
826 labels: CategoryLabels,
831 impl MultipleResponseType {
835 input: &raw::MultipleResponseType,
837 warn: &impl Fn(Error),
838 ) -> Result<Self, Error> {
839 let mr_type = match input {
840 raw::MultipleResponseType::MultipleDichotomy { value, labels } => {
841 let value = decoder.decode_string(&value.0, warn);
842 let value = match min_width {
843 VarWidth::Numeric => {
844 let number: f64 = value.trim().parse()
845 .map_err(|_| Error::InvalidMDGroupCountedValue { mr_set: mr_set.clone(), number: value.into() })?;
846 Value::Number(Some(number.into()))
848 VarWidth::String(max_width) => {
849 let value = value.trim_end_matches(' ');
850 let width = value.len();
851 if width > max_width as usize {
852 return Err(Error::TooWideMDGroupCountedValue { mr_set: mr_set.clone(), value: value.into(), width, max_width });
854 Value::String(value.into())
857 MultipleResponseType::MultipleDichotomy { value, labels: *labels }
859 raw::MultipleResponseType::MultipleCategory => MultipleResponseType::MultipleCategory,
865 #[derive(Clone, Debug)]
866 pub struct MultipleResponseSet {
867 pub name: Identifier,
868 pub min_width: VarWidth,
869 pub max_width: VarWidth,
871 pub mr_type: MultipleResponseType,
872 pub dict_indexes: Vec<DictIndex>,
875 impl MultipleResponseSet {
878 input: &raw::MultipleResponseSet,
879 warn: &impl Fn(Error),
880 ) -> Result<Self, Error> {
881 let mr_set_name = decoder
882 .decode_identifier(&input.name.0, warn)
883 .map_err(|error| Error::InvalidMrSetName(error))?;
885 let label = decoder.decode_string(&input.label.0, warn).into();
887 let mut dict_indexes = Vec::with_capacity(input.short_names.len());
888 for short_name in input.short_names.iter() {
889 let short_name = match decoder.decode_identifier(&short_name.0, warn) {
892 warn(Error::InvalidMrSetName(error));
896 let Some(&dict_index) = decoder.var_names.get(&short_name) else {
897 warn(Error::UnknownMrSetVariable {
898 mr_set: mr_set_name.clone(),
899 short_name: short_name.clone(),
903 dict_indexes.push(dict_index);
906 match dict_indexes.len() {
907 0 => return Err(Error::EmptyMrSet(mr_set_name)),
908 1 => return Err(Error::OneVarMrSet(mr_set_name)),
912 let Some((Some(min_width), Some(max_width))) = dict_indexes
914 .map(|dict_index| decoder.variables[dict_index].width)
915 .map(|w| (Some(w), Some(w)))
916 .reduce(|(na, wa), (nb, wb)| (VarWidth::narrower(na, nb), VarWidth::wider(wa, wb)))
918 return Err(Error::MixedMrSet(mr_set_name));
921 let mr_type = MultipleResponseType::decode(decoder, &mr_set_name, &input.mr_type, min_width, warn)?;
923 Ok(MultipleResponseSet {
934 #[derive(Clone, Debug)]
935 pub struct MultipleResponseRecord(pub Vec<MultipleResponseSet>);
937 impl Decode for MultipleResponseRecord {
938 type Input = raw::MultipleResponseRecord;
940 fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error> {
941 let mut sets = Vec::with_capacity(input.0.len());
942 for set in &input.0 {
943 match MultipleResponseSet::decode(decoder, set, &warn) {
944 Ok(set) => sets.push(set),
945 Err(error) => warn(error),
948 Ok(MultipleResponseRecord(sets))
954 use encoding_rs::WINDOWS_1252;
958 let mut s = String::new();
959 s.push(char::REPLACEMENT_CHARACTER);
960 let encoded = WINDOWS_1252.encode(&s).0;
961 let decoded = WINDOWS_1252.decode(&encoded[..]).0;
962 println!("{:?}", decoded);
967 let charset: Vec<u8> = (0..=255).collect();
968 println!("{}", charset.len());
969 let decoded = WINDOWS_1252.decode(&charset[..]).0;
970 println!("{}", decoded.len());
971 let encoded = WINDOWS_1252.encode(&decoded[..]).0;
972 println!("{}", encoded.len());
973 assert_eq!(&charset[..], &encoded[..]);