separate sack integration test binary
[pspp] / rust / tests / sack.rs
1 use std::fs::read_to_string;
2 use std::io::{stdout, IsTerminal, Write};
3 use std::path::PathBuf;
4
5 use anyhow::{anyhow, Result};
6 use clap::Parser;
7 use pspp::endian::Endian;
8 use pspp::sack::sack;
9
10 /// SAv Construction Kit
11 ///
12 /// The input is a sequence of data items, each followed by a semicolon.  Each
13 /// data item is converted to the output format and written on stdout.  A data
14 /// item is one of the following:
15 ///
16 ///   - An integer in decimal, in hexadecimal prefixed by `0x`, or in octal
17 ///     prefixed by `0`.  Output as a 32-bit binary integer.
18 ///
19 ///   - A floating-point number.  Output in 64-bit IEEE 754 format.
20 ///
21 ///   - A string enclosed in double quotes.  Output literally.  There is no
22 ///     syntax for "escapes".  Strings may not contain new-lines.
23 ///
24 ///   - A literal of the form `s<number>` followed by a quoted string as above.
25 ///     Output as the string's contents followed by enough spaces to fill up
26 ///     `<number>` bytes.  For example, `s8 "foo"` is output as `foo` followed
27 ///     by 5 spaces.
28 ///
29 ///   - The literal `i8`, `i16`, or `i64` followed by an integer.  Output
30 ///     as a binary integer with the specified number of bits.
31 ///
32 ///   - One of the literals `SYSMIS`, `LOWEST`, or `HIGHEST`.  Output as a
33 ///     64-bit IEEE 754 float of the appropriate PSPP value.
34 ///
35 ///   - `PCSYSMIS`.  Output as SPSS/PC+ system-missing value.
36 ///
37 ///   - The literal `ENDIAN`.  Output as a 32-bit binary integer, either with
38 ///     value 1 if `--be` is in effect or 2 if `--le` is in effect.
39 ///
40 ///   - A pair of parentheses enclosing a sequence of data items, each followed
41 ///     by a semicolon (the last semicolon is optional).  Output as the enclosed
42 ///     data items in sequence.
43 ///
44 ///   - The literal `COUNT` or `COUNT8` followed by a sequence of parenthesized
45 ///     data items, as above.  Output as a 32-bit or 8-bit binary integer whose
46 ///     value is the number of bytes enclosed within the parentheses, followed
47 ///     by the enclosed data items themselves.
48 ///
49 /// optionally followed by an asterisk and a positive integer, which specifies a
50 /// repeat count for the data item.
51 #[derive(Parser, Debug)]
52 struct Args {
53     /// Big-endian output format (default)
54     #[arg(long = "be")]
55     be: bool,
56
57     /// Little-endian output format
58     #[arg(long = "le")]
59     le: bool,
60
61     /// Input file.
62     #[arg(required = true)]
63     input: PathBuf,
64 }
65
66 fn main() -> Result<()> {
67     let Args { be, le, input } = Args::parse();
68     if stdout().is_terminal() {
69         return Err(anyhow!(
70             "not writing binary data to a terminal; redirect to a file"
71         ));
72     }
73     let endian = match (be, le) {
74         (false, false) | (true, false) => Endian::Big,
75         (false, true) => Endian::Little,
76         (true, true) => return Err(anyhow!("can't use both `--be` and `--le`")),
77     };
78     let input = read_to_string(&input)?;
79     let output = sack(&input, endian)?;
80     stdout().write(&output)?;
81     Ok(())
82 }