02f5c23d79d079a6e1509959833433821375868c
[pspp] / rust / src / cooked.rs
1 use std::{borrow::Cow, cmp::Ordering, collections::HashMap, iter::repeat};
2
3 use crate::{
4     format::{Error as FormatError, Spec, UncheckedSpec},
5     identifier::{Error as IdError, Identifier},
6     raw::{self, MissingValues, VarType},
7     {endian::Endian, Compression},
8 };
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;
14
15 #[derive(ThisError, Debug)]
16 pub enum Error {
17     #[error("Variable record at offset {offset:#x} specifies width {width} not in valid range [-1,255).")]
18     InvalidVariableWidth { offset: u64, width: i32 },
19
20     #[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.")]
21     InvalidLongMissingValueFormat,
22
23     #[error("File creation date {creation_date} is not in the expected format \"DD MMM YY\" format.  Using 01 Jan 1970.")]
24     InvalidCreationDate { creation_date: String },
25
26     #[error("File creation time {creation_time} is not in the expected format \"HH:MM:SS\" format.  Using midnight.")]
27     InvalidCreationTime { creation_time: String },
28
29     #[error("{id_error}  Renaming variable to {new_name}.")]
30     InvalidVariableName {
31         id_error: IdError,
32         new_name: Identifier,
33     },
34
35     #[error(
36         "Substituting {new_spec} for invalid print format on variable {variable}.  {format_error}"
37     )]
38     InvalidPrintFormat {
39         new_spec: Spec,
40         variable: Identifier,
41         format_error: FormatError,
42     },
43
44     #[error(
45         "Substituting {new_spec} for invalid write format on variable {variable}.  {format_error}"
46     )]
47     InvalidWriteFormat {
48         new_spec: Spec,
49         variable: Identifier,
50         format_error: FormatError,
51     },
52
53     #[error("Renaming variable with duplicate name {duplicate_name} to {new_name}.")]
54     DuplicateVariableName {
55         duplicate_name: Identifier,
56         new_name: Identifier,
57     },
58
59     #[error("Dictionary index {dict_index} is outside valid range [1,{max_index}].")]
60     InvalidDictIndex { dict_index: usize, max_index: usize },
61
62     #[error("Dictionary index {0} refers to a long string continuation.")]
63     DictIndexIsContinuation(usize),
64
65     #[error("Variables associated with value label are not all of identical type.  Variable {numeric_var} is numeric, but variable {string_var} is string.")]
66     ValueLabelsDifferentTypes {
67         numeric_var: Identifier,
68         string_var: Identifier,
69     },
70
71     #[error(
72         "Value labels may not be added to long string variable {0} using record types 3 or 4."
73     )]
74     InvalidLongStringValueLabel(Identifier),
75
76     #[error("Details TBD")]
77     TBD,
78 }
79
80 #[derive(Clone, Debug)]
81 pub enum Record {
82     Header(HeaderRecord),
83     Variable(VariableRecord),
84     ValueLabel(ValueLabelRecord),
85     Document(DocumentRecord),
86     IntegerInfo(IntegerInfoRecord),
87     FloatInfo(FloatInfoRecord),
88     VariableSets(VariableSetRecord),
89     VarDisplay(VarDisplayRecord),
90     //MultipleResponse(MultipleResponseRecord),
91     //LongStringValueLabels(LongStringValueLabelRecord),
92     Encoding(EncodingRecord),
93     NumberOfCases(NumberOfCasesRecord),
94     ProductInfo(ProductInfoRecord),
95     //LongNames(UnencodedString),
96     //LongStrings(UnencodedString),
97     //FileAttributes(UnencodedString),
98     //VariableAttributes(UnencodedString),
99     //OtherExtension(Extension),
100     //EndOfHeaders(u32),
101     //ZHeader(ZHeader),
102     //ZTrailer(ZTrailer),
103     //Case(Vec<Value>),
104 }
105
106 pub use crate::raw::IntegerInfoRecord;
107 pub use crate::raw::FloatInfoRecord;
108 pub use crate::raw::EncodingRecord;
109 pub use crate::raw::NumberOfCasesRecord;
110
111 type DictIndex = usize;
112
113 pub struct Variable {
114     pub dict_index: DictIndex,
115     pub short_name: Identifier,
116     pub long_name: Option<Identifier>,
117     pub width: VarWidth,
118 }
119
120 pub struct Decoder {
121     pub compression: Option<Compression>,
122     pub endian: Endian,
123     pub encoding: &'static Encoding,
124     pub variables: HashMap<DictIndex, Variable>,
125     pub var_names: HashMap<Identifier, DictIndex>,
126     n_dict_indexes: usize,
127     n_generated_names: usize,
128 }
129
130 impl Decoder {
131     fn generate_name(&mut self) -> Identifier {
132         loop {
133             self.n_generated_names += 1;
134             let name = Identifier::new(&format!("VAR{:03}", self.n_generated_names), self.encoding)
135                 .unwrap();
136             if !self.var_names.contains_key(&name) {
137                 return name;
138             }
139             assert!(self.n_generated_names < usize::MAX);
140         }
141     }
142     fn decode_string<'a>(&self, input: &'a [u8], warn: &impl Fn(Error)) -> Cow<'a, str> {
143         let (output, malformed) = self.encoding.decode_without_bom_handling(input);
144         if malformed {
145             warn(Error::TBD);
146         }
147         output
148     }
149     fn get_var_by_index(&self, dict_index: usize) -> Result<&Variable, Error> {
150         let max_index = self.n_dict_indexes - 1;
151         if dict_index == 0 || dict_index as usize > max_index {
152             return Err(Error::InvalidDictIndex {
153                 dict_index,
154                 max_index,
155             });
156         }
157         let Some(variable) = self.variables.get(&dict_index) else {
158             return Err(Error::DictIndexIsContinuation(dict_index));
159         };
160         Ok(variable)
161     }
162
163     /// Returns `input` decoded from `self.encoding` into UTF-8 such that
164     /// re-encoding the result back into `self.encoding` will have exactly the
165     /// same length in bytes.
166     ///
167     /// XXX warn about errors?
168     fn decode_exact_length<'a>(&self, input: &'a [u8]) -> Cow<'a, str> {
169         if let (s, false) = self.encoding.decode_without_bom_handling(input) {
170             // This is the common case.  Usually there will be no errors.
171             s.into()
172         } else {
173             // Unusual case.  Don't bother to optimize it much.
174             let mut decoder = self.encoding.new_decoder_without_bom_handling();
175             let mut output = String::with_capacity(
176                 decoder
177                     .max_utf8_buffer_length_without_replacement(input.len())
178                     .unwrap(),
179             );
180             let mut rest = input;
181             while !rest.is_empty() {
182                 match decoder.decode_to_string_without_replacement(rest, &mut output, true) {
183                     (DecoderResult::InputEmpty, _) => break,
184                     (DecoderResult::OutputFull, _) => unreachable!(),
185                     (DecoderResult::Malformed(a, b), consumed) => {
186                         let skipped = a as usize + b as usize;
187                         output.extend(repeat('?').take(skipped));
188                         rest = &rest[consumed..];
189                     }
190                 }
191             }
192             assert_eq!(self.encoding.encode(&output).0.len(), input.len());
193             output.into()
194         }
195     }
196 }
197
198 pub trait Decode: Sized {
199     type Input;
200     fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error>;
201 }
202
203 #[derive(Clone, Debug)]
204 pub struct HeaderRecord {
205     pub eye_catcher: String,
206     pub weight_index: Option<usize>,
207     pub n_cases: Option<u64>,
208     pub creation: NaiveDateTime,
209     pub file_label: String,
210 }
211
212 impl Decode for HeaderRecord {
213     type Input = crate::raw::HeaderRecord;
214
215     fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error> {
216         let eye_catcher = decoder.decode_string(&input.eye_catcher.0, &warn);
217         let file_label = decoder.decode_string(&input.file_label.0, &warn);
218         let creation_date = decoder.decode_string(&input.creation_date.0, &warn);
219         let creation_date = NaiveDate::parse_from_str(&creation_date, "%v").unwrap_or_else(|_| {
220             warn(Error::InvalidCreationDate {
221                 creation_date: creation_date.into(),
222             });
223             Default::default()
224         });
225         let creation_time = decoder.decode_string(&input.creation_time.0, &warn);
226         let creation_time =
227             NaiveTime::parse_from_str(&creation_time, "%H:%M:%S").unwrap_or_else(|_| {
228                 warn(Error::InvalidCreationTime {
229                     creation_time: creation_time.into(),
230                 });
231                 Default::default()
232             });
233         Ok(HeaderRecord {
234             eye_catcher: eye_catcher.into(),
235             weight_index: input.weight_index.map(|n| n as usize),
236             n_cases: input.n_cases.map(|n| n as u64),
237             creation: NaiveDateTime::new(creation_date, creation_time),
238             file_label: file_label.into(),
239         })
240     }
241 }
242
243 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
244 pub enum VarWidth {
245     Numeric,
246     String(u16),
247 }
248
249 impl PartialOrd for VarWidth {
250     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
251         match (self, other) {
252             (VarWidth::Numeric, VarWidth::Numeric) => Some(Ordering::Equal),
253             (VarWidth::String(a), VarWidth::String(b)) => Some(a.cmp(b)),
254             _ => None,
255         }
256     }
257 }
258
259 impl VarWidth {
260     fn n_dict_indexes(self) -> usize {
261         match self {
262             VarWidth::Numeric => 1,
263             VarWidth::String(w) => div_ceil(w as usize, 8),
264         }
265     }
266 }
267
268 impl From<VarWidth> for VarType {
269     fn from(source: VarWidth) -> Self {
270         match source {
271             VarWidth::Numeric => VarType::Numeric,
272             VarWidth::String(_) => VarType::String,
273         }
274     }
275 }
276
277 #[derive(Clone, Debug)]
278 pub struct VariableRecord {
279     pub width: VarWidth,
280     pub name: Identifier,
281     pub print_format: Spec,
282     pub write_format: Spec,
283     pub missing_values: MissingValues,
284     pub label: Option<String>,
285 }
286
287 fn decode_format(raw: raw::Spec, width: VarWidth, warn: impl Fn(Spec, FormatError)) -> Spec {
288     UncheckedSpec::try_from(raw)
289         .and_then(Spec::try_from)
290         .and_then(|x| x.check_width_compatibility(width))
291         .unwrap_or_else(|error| {
292             let new_format = Spec::default_for_width(width);
293             warn(new_format, error);
294             new_format
295         })
296 }
297
298 impl VariableRecord {
299     pub fn decode(
300         decoder: &mut Decoder,
301         input: &crate::raw::VariableRecord,
302         warn: impl Fn(Error),
303     ) -> Result<Option<VariableRecord>, Error> {
304         let width = match input.width {
305             0 => VarWidth::Numeric,
306             w @ 1..=255 => VarWidth::String(w as u16),
307             -1 => return Ok(None),
308             _ => {
309                 return Err(Error::InvalidVariableWidth {
310                     offset: input.offset,
311                     width: input.width,
312                 })
313             }
314         };
315         let name = decoder.decode_string(&input.name.0, &warn);
316         let name = match Identifier::new(&name, decoder.encoding) {
317             Ok(name) => {
318                 if !decoder.var_names.contains_key(&name) {
319                     name
320                 } else {
321                     let new_name = decoder.generate_name();
322                     warn(Error::DuplicateVariableName {
323                         duplicate_name: name.clone(),
324                         new_name: new_name.clone(),
325                     });
326                     new_name
327                 }
328             }
329             Err(id_error) => {
330                 let new_name = decoder.generate_name();
331                 warn(Error::InvalidVariableName {
332                     id_error,
333                     new_name: new_name.clone(),
334                 });
335                 new_name
336             }
337         };
338         let variable = Variable {
339             dict_index: decoder.n_dict_indexes,
340             short_name: name.clone(),
341             long_name: None,
342             width,
343         };
344         decoder.n_dict_indexes += width.n_dict_indexes();
345         assert!(decoder
346             .var_names
347             .insert(name.clone(), variable.dict_index)
348             .is_none());
349         assert!(decoder
350             .variables
351             .insert(variable.dict_index, variable)
352             .is_none());
353
354         let print_format = decode_format(input.print_format, width, |new_spec, format_error| {
355             warn(Error::InvalidPrintFormat {
356                 new_spec,
357                 variable: name.clone(),
358                 format_error,
359             })
360         });
361         let write_format = decode_format(input.write_format, width, |new_spec, format_error| {
362             warn(Error::InvalidWriteFormat {
363                 new_spec,
364                 variable: name.clone(),
365                 format_error,
366             })
367         });
368         let label = input
369             .label
370             .as_ref()
371             .map(|label| decoder.decode_string(&label.0, &warn).into());
372         Ok(Some(VariableRecord {
373             width,
374             name,
375             print_format,
376             write_format,
377             missing_values: input.missing_values.clone(),
378             label,
379         }))
380     }
381 }
382
383 #[derive(Clone, Debug)]
384 pub struct DocumentRecord(Vec<String>);
385
386 impl Decode for DocumentRecord {
387     type Input = crate::raw::DocumentRecord;
388
389     fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error> {
390         Ok(DocumentRecord(
391             input
392                 .lines
393                 .iter()
394                 .map(|s| decoder.decode_string(&s.0, &warn).into())
395                 .collect(),
396         ))
397     }
398 }
399
400 trait TextRecord
401 where
402     Self: Sized,
403 {
404     const NAME: &'static str;
405     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error>;
406 }
407
408 #[derive(Clone, Debug)]
409 pub struct VariableSet {
410     pub name: String,
411     pub vars: Vec<String>,
412 }
413
414 impl VariableSet {
415     fn parse(input: &str) -> Result<Self, Error> {
416         let (name, input) = input.split_once('=').ok_or(Error::TBD)?;
417         let vars = input.split_ascii_whitespace().map(String::from).collect();
418         Ok(VariableSet {
419             name: name.into(),
420             vars,
421         })
422     }
423 }
424
425 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
426 pub enum Value {
427     Number(Option<OrderedFloat<f64>>),
428     String(String),
429 }
430
431 impl Value {
432     pub fn decode(raw: raw::Value, decoder: &Decoder) -> Self {
433         match raw {
434             raw::Value::Number(x) => Value::Number(x.map(|x| x.into())),
435             raw::Value::String(s) => Value::String(decoder.decode_exact_length(&s.0).into()),
436         }
437     }
438 }
439
440 #[derive(Clone, Debug)]
441 pub struct ValueLabelRecord {
442     pub var_type: VarType,
443     pub labels: Vec<(Value, String)>,
444     pub variables: Vec<Identifier>,
445 }
446
447 trait WarnOnError<T> {
448     fn warn_on_error<F: Fn(Error)>(self, warn: &F) -> Option<T>;
449 }
450 impl<T> WarnOnError<T> for Result<T, Error> {
451     fn warn_on_error<F: Fn(Error)>(self, warn: &F) -> Option<T> {
452         match self {
453             Ok(result) => Some(result),
454             Err(error) => {
455                 warn(error);
456                 None
457             }
458         }
459     }
460 }
461
462 impl ValueLabelRecord {
463     pub fn decode(
464         decoder: &mut Decoder,
465         raw_value_label: &crate::raw::ValueLabelRecord,
466         dict_indexes: &crate::raw::VarIndexRecord,
467         warn: impl Fn(Error),
468     ) -> Result<Option<ValueLabelRecord>, Error> {
469         let variables: Vec<&Variable> = dict_indexes
470             .dict_indexes
471             .iter()
472             .filter_map(|&dict_index| {
473                 decoder
474                     .get_var_by_index(dict_index as usize)
475                     .warn_on_error(&warn)
476             })
477             .filter(|&variable| match variable.width {
478                 VarWidth::String(width) if width > 8 => {
479                     warn(Error::InvalidLongStringValueLabel(
480                         variable.short_name.clone(),
481                     ));
482                     false
483                 }
484                 _ => true,
485             })
486             .collect();
487         let mut i = variables.iter();
488         let Some(&first_var) = i.next() else {
489             return Ok(None);
490         };
491         let var_type: VarType = first_var.width.into();
492         for &variable in i {
493             let this_type: VarType = variable.width.into();
494             if var_type != this_type {
495                 let (numeric_var, string_var) = match var_type {
496                     VarType::Numeric => (first_var, variable),
497                     VarType::String => (variable, first_var),
498                 };
499                 warn(Error::ValueLabelsDifferentTypes {
500                     numeric_var: numeric_var.short_name.clone(),
501                     string_var: string_var.short_name.clone(),
502                 });
503                 return Ok(None);
504             }
505         }
506         let labels = raw_value_label
507             .labels
508             .iter()
509             .map(|(value, label)| {
510                 let label = decoder.decode_string(&label.0, &warn);
511                 let value = Value::decode(raw::Value::from_raw(*value, var_type, decoder.endian), &decoder);
512                 (value, label.into())
513             })
514             .collect();
515         let variables = variables
516             .iter()
517             .map(|&variable| variable.short_name.clone())
518             .collect();
519         Ok(Some(ValueLabelRecord {
520             var_type,
521             labels,
522             variables,
523         }))
524     }
525 }
526
527 #[derive(Clone, Debug)]
528 pub struct VariableSetRecord(Vec<VariableSet>);
529
530 impl TextRecord for VariableSetRecord {
531     const NAME: &'static str = "variable set";
532     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
533         let mut sets = Vec::new();
534         for line in input.lines() {
535             if let Some(set) = VariableSet::parse(line).warn_on_error(&warn) {
536                 sets.push(set)
537             }
538         }
539         Ok(VariableSetRecord(sets))
540     }
541 }
542
543 #[derive(Clone, Debug)]
544 pub struct ProductInfoRecord(pub String);
545
546 impl TextRecord for ProductInfoRecord {
547     const NAME: &'static str = "extra product info";
548     fn parse(input: &str, _warn: impl Fn(Error)) -> Result<Self, Error> {
549         Ok(ProductInfoRecord(input.into()))
550     }
551 }
552
553 pub struct LongVariableName {
554     pub short_name: String,
555     pub long_name: String,
556 }
557
558 pub struct LongVariableNameRecord(Vec<LongVariableName>);
559
560 impl TextRecord for LongVariableNameRecord {
561     const NAME: &'static str = "long variable names";
562     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
563         let mut names = Vec::new();
564         for pair in input.split('\t').filter(|s| !s.is_empty()) {
565             if let Some((short_name, long_name)) = pair.split_once('=') {
566                 let name = LongVariableName {
567                     short_name: short_name.into(),
568                     long_name: long_name.into(),
569                 };
570                 names.push(name);
571             } else {
572                 warn(Error::TBD)
573             }
574         }
575         Ok(LongVariableNameRecord(names))
576     }
577 }
578
579 pub struct VeryLongString {
580     pub short_name: String,
581     pub length: usize,
582 }
583
584 impl VeryLongString {
585     fn parse(input: &str) -> Result<VeryLongString, Error> {
586         let Some((short_name, length)) = input.split_once('=') else {
587             return Err(Error::TBD);
588         };
589         let length: usize = length.parse().map_err(|_| Error::TBD)?;
590         Ok(VeryLongString {
591             short_name: short_name.into(),
592             length,
593         })
594     }
595 }
596
597 pub struct VeryLongStringRecord(Vec<VeryLongString>);
598
599 impl TextRecord for VeryLongStringRecord {
600     const NAME: &'static str = "very long strings";
601     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
602         let mut very_long_strings = Vec::new();
603         for tuple in input
604             .split('\0')
605             .map(|s| s.trim_end_matches('\t'))
606             .filter(|s| !s.is_empty())
607         {
608             if let Some(vls) = VeryLongString::parse(tuple).warn_on_error(&warn) {
609                 very_long_strings.push(vls)
610             }
611         }
612         Ok(VeryLongStringRecord(very_long_strings))
613     }
614 }
615
616 pub struct Attribute {
617     pub name: String,
618     pub values: Vec<String>,
619 }
620
621 impl Attribute {
622     fn parse<'a>(input: &'a str, warn: &impl Fn(Error)) -> Result<(Attribute, &'a str), Error> {
623         let Some((name, mut input)) = input.split_once('(') else {
624             return Err(Error::TBD);
625         };
626         let mut values = Vec::new();
627         loop {
628             let Some((value, rest)) = input.split_once('\n') else {
629                 return Err(Error::TBD);
630             };
631             if let Some(stripped) = value
632                 .strip_prefix('\'')
633                 .and_then(|value| value.strip_suffix('\''))
634             {
635                 values.push(stripped.into());
636             } else {
637                 warn(Error::TBD);
638                 values.push(value.into());
639             }
640             if let Some(rest) = rest.strip_prefix(')') {
641                 return Ok((
642                     Attribute {
643                         name: name.into(),
644                         values,
645                     },
646                     rest,
647                 ));
648             }
649             input = rest;
650         }
651     }
652 }
653
654 pub struct AttributeSet(pub Vec<Attribute>);
655
656 impl AttributeSet {
657     fn parse<'a>(
658         mut input: &'a str,
659         sentinel: Option<char>,
660         warn: &impl Fn(Error),
661     ) -> Result<(AttributeSet, &'a str), Error> {
662         let mut attributes = Vec::new();
663         let rest = loop {
664             match input.chars().next() {
665                 None => break input,
666                 c if c == sentinel => break &input[1..],
667                 _ => {
668                     let (attribute, rest) = Attribute::parse(input, &warn)?;
669                     attributes.push(attribute);
670                     input = rest;
671                 }
672             }
673         };
674         Ok((AttributeSet(attributes), rest))
675     }
676 }
677
678 pub struct FileAttributeRecord(AttributeSet);
679
680 impl TextRecord for FileAttributeRecord {
681     const NAME: &'static str = "data file attributes";
682     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
683         let (set, rest) = AttributeSet::parse(input, None, &warn)?;
684         if !rest.is_empty() {
685             warn(Error::TBD);
686         }
687         Ok(FileAttributeRecord(set))
688     }
689 }
690
691 pub struct VarAttributeSet {
692     pub long_var_name: String,
693     pub attributes: AttributeSet,
694 }
695
696 impl VarAttributeSet {
697     fn parse<'a>(
698         input: &'a str,
699         warn: &impl Fn(Error),
700     ) -> Result<(VarAttributeSet, &'a str), Error> {
701         let Some((long_var_name, rest)) = input.split_once(':') else {
702             return Err(Error::TBD);
703         };
704         let (attributes, rest) = AttributeSet::parse(rest, Some('/'), warn)?;
705         Ok((
706             VarAttributeSet {
707                 long_var_name: long_var_name.into(),
708                 attributes,
709             },
710             rest,
711         ))
712     }
713 }
714
715 pub struct VariableAttributeRecord(Vec<VarAttributeSet>);
716
717 impl TextRecord for VariableAttributeRecord {
718     const NAME: &'static str = "variable attributes";
719     fn parse(mut input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
720         let mut var_attribute_sets = Vec::new();
721         while !input.is_empty() {
722             let Some((var_attribute, rest)) =
723                 VarAttributeSet::parse(input, &warn).warn_on_error(&warn)
724             else {
725                 break;
726             };
727             var_attribute_sets.push(var_attribute);
728             input = rest;
729         }
730         Ok(VariableAttributeRecord(var_attribute_sets))
731     }
732 }
733
734 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
735 pub enum Measure {
736     Nominal,
737     Ordinal,
738     Scale,
739 }
740
741 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
742 pub enum Alignment {
743     Left,
744     Right,
745     Center,
746 }
747
748 #[derive(Clone, Debug)]
749 pub struct VarDisplay {
750     pub measure: Option<Measure>,
751     pub width: u32,
752     pub align: Option<Alignment>,
753 }
754
755 #[derive(Clone, Debug)]
756 pub struct VarDisplayRecord(pub Vec<VarDisplay>);
757
758 #[cfg(test)]
759 mod test {
760     use encoding_rs::WINDOWS_1252;
761
762     #[test]
763     fn test() {
764         let mut s = String::new();
765         s.push(char::REPLACEMENT_CHARACTER);
766         let encoded = WINDOWS_1252.encode(&s).0;
767         let decoded = WINDOWS_1252.decode(&encoded[..]).0;
768         println!("{:?}", decoded);
769     }
770
771     #[test]
772     fn test2() {
773         let charset: Vec<u8> = (0..=255).collect();
774         println!("{}", charset.len());
775         let decoded = WINDOWS_1252.decode(&charset[..]).0;
776         println!("{}", decoded.len());
777         let encoded = WINDOWS_1252.encode(&decoded[..]).0;
778         println!("{}", encoded.len());
779         assert_eq!(&charset[..], &encoded[..]);
780     }
781 }