1#[allow(dead_code)]
2use camino::Utf8Path;
3use lex::SPCReader;
4use miette::IntoDiagnostic;
5use parse::ParsedSPC;
6
7mod block;
8mod header;
9mod lex;
10mod logblock;
11mod parse;
12pub(crate) mod units;
13mod write;
14
15use lex::LexedSPC;
16use parse::TryParse;
17use units::{
18 xzwType, xzwTypeCreationError, yType, yTypeCreationError, InstrumentTechnique,
19 InstrumentTechniqueCreationError,
20};
21use write::{CsvWriter, WriteSPC};
22use zerocopy::{BigEndian, LittleEndian};
23
24pub fn write_spc(input_path: &Utf8Path, parsed: ParsedSPC) -> miette::Result<()> {
25 let output_path = input_path.with_extension("csv");
26 let writer = CsvWriter;
27
28 let mut file_handle = fs_err::OpenOptions::new()
29 .write(true)
30 .create(true)
31 .open(output_path)
32 .into_diagnostic()?;
33
34 writer
35 .write_spc(&mut file_handle, &parsed)
36 .into_diagnostic()
37}
38
39pub fn parse(source: &'_ [u8]) -> miette::Result<ParsedSPC> {
40 Ok(match source.get(1).copied() {
41 Some(0x4c) => lex_big_endian_spc(source)?.try_parse(),
42 Some(0x4b) | Some(0x4d) => lex_little_endian_spc(source)?.try_parse(),
43 Some(b) => panic!("impossible file type descriptor {b}"),
44 None => panic!("file contained less than two bytes"),
45 }?)
46}
47
48pub fn lex_big_endian_spc(source: &'_ [u8]) -> miette::Result<LexedSPC<'_, BigEndian>> {
49 log::info!("lexing big-endian SPC file");
50 assert!(matches!(source.get(1).copied().unwrap(), 0x4c));
51 SPCReader::big_endian(source).lex()
52}
53
54pub fn lex_little_endian_spc(source: &'_ [u8]) -> miette::Result<LexedSPC<'_, LittleEndian>> {
55 log::info!("lexing little-endian SPC file");
56 assert!(matches!(source.get(1).copied().unwrap(), 0x4b | 0x4d));
57 SPCReader::little_endian(source).lex()
58}
59
60#[cfg(test)]
61mod test {
62 use std::io::{BufReader, Cursor, Read};
63
64 use fs_err::File;
65 use miette::{Context, IntoDiagnostic};
66
67 use crate::{
68 parse,
69 write::{CsvWriter, WriteSPC},
70 };
71
72 #[test]
73 fn ftir_data_parse_matches_expected() -> miette::Result<()> {
74 let expected = include_str!("/tmp/spc/test_data/Ft-ir.csv");
75
76 let spc = "/tmp/spc/test_data/Ft-ir.spc";
77
78 let file = File::open(spc)
79 .into_diagnostic()
80 .wrap_err_with(|| format!("opening '{}' failed", spc))?;
81
82 let source = BufReader::new(file)
83 .bytes()
84 .collect::<Result<Vec<_>, _>>()
85 .unwrap();
86
87 let parsed = parse(&source[..])?;
88
89 let writer = CsvWriter;
90 let mut sink: Cursor<Vec<u8>> = Cursor::new(Vec::new());
91
92 writer.write_spc(&mut sink, &parsed).unwrap();
93
94 let output_vec: Vec<u8> = sink.into_inner();
95 let output_string: String = String::from_utf8(output_vec).unwrap();
96
97 for (expected, actual) in expected
98 .split_terminator('\n')
99 .zip(output_string.split_terminator('\n'))
100 {
101 assert_eq!(expected, actual);
102 }
103
104 Ok(())
105 }
106}