ot_tools_io/lib.rs
1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6//! Serialization and Deserialization library for Elektron Octatrack data files.
7//!
8//! ## Important types
9//!
10//! | rust type | octatrack filename pattern | description |
11//! | ------------ | -------------------------- | ------------|
12//! | [crate::arrangements::ArrangementFile] | `arr??.*` | data for arrangements |
13//! | [crate::banks::BankFile] | `bank??.*` | data for parts and patterns |
14//! | [crate::markers::MarkersFile] | `markers.*` | start trim/end trim/slices/loop points for sample slots |
15//! | [crate::projects::ProjectFile] | `project.*` | project level settings; state; sample slots |
16//! | [crate::samples::SampleAttributes] | `*.ot` | saved sample settings data, loops slices etc. |
17//!
18//!
19//! Only the above types implement the [crate::Encode] and [crate::Decode] traits,
20//! which means only these types can be read from / written to the filesystem using functions
21//! in this library.
22//!
23//! Read the relevant modules in this library for more detailed information on the data contained in each file.
24//!
25//! ## How do different Octatrack data files relate to each other?
26//!
27//! - A Bank stores zero-indexed sample slot IDs to indicate which sample should be played when on a given track
28//! (part machine data and/or track p-lock trigs).
29//! - Changing the sample loaded into a sample slot updates both the `project.*` file (change the trig quantization
30//! settings, file path etc) and the `markers.*` file (change the trim settings based on either initial load,
31//! or any data in a relevant `*.ot` sample settings file).
32//! - Data from `project.*`and `markers.*` is written to an `*.ot` file when saving sample attributes data from the
33//! octatrack's audio editing menu.
34//! - Loading a sample into a project sample slot (`project.*` and `markers.*` files) reads any data in an `*.ot` files
35//! and configures the sample slot accordingly.
36//!
37//! ## When do `*.work` and `*.strd` files get modified by the Octatrack?
38//!
39//! - `*.work` files are created when creating a new project `PROJECT MENU -> CHANGE PROJECT -> CREATE NEW`.
40//! - `*.work` files are updated by using the `PROJECT MENU -> SYNC TO CARD` operation.
41//! - `*.work` files are updated when the user performs a `PROJECT MENU -> SAVE PROJECT` operation.
42//! - `*.strd` files are created/updated when the user performs a `PROJECT MENU -> SAVE PROJECT` operation.
43//! - `*.strd` files are not changed by the `PROJECT -> SYNC TO CARD` operation.
44//! - `arr??.strd` files can also be saved via the `ARRANGER MENU -> SAVE ARRANGEMENT` operation.
45//!
46//! ## Notable 'gotcha's
47//! - Project sample slots (`project.*` files) are one-indexed, but their references everywhere else are zero-indexed
48//! (`bank??.*` and `markers.*` files).
49//! - A 'Disabled' loop point in either `markers.*` or `*.ot` files is a `0xFFFFFFFF` value, but the default
50//! loop point when creating new `markers.*` file is always `0_u32`. The 'Disabled' value setting is only set
51//! when a sample is loaded into a sample slot.
52//!
53//! ## Example code
54//! ```rust
55//! /*
56//! reading, mutating and writing a bank file
57//! */
58//!
59//! use std::path::PathBuf;
60//! use ot_tools_io::{read_type_from_bin_file, write_type_to_bin_file, banks::BankFile};
61//!
62//! let binfpath = PathBuf::from("test-data")
63//! .join("blank-project")
64//! .join("bank01.work");
65//!
66//! // read an editable version of the bank file
67//! let mut bank: BankFile = read_type_from_bin_file(&binfpath).unwrap();
68//!
69//! // change active scenes on the working copy of Part 4
70//! bank.parts.unsaved[3].active_scenes.scene_a = 2;
71//! bank.parts.unsaved[3].active_scenes.scene_b = 6;
72//!
73//! // write to a new bank file
74//! let outfpath = std::env::temp_dir()
75//! .join("ot-tools-io")
76//! .join("doctest")
77//! .join("main_example_1");
78//! write_type_to_bin_file::<BankFile>(&bank, &outfpath).unwrap();
79//! ```
80
81pub mod arrangements;
82pub mod banks;
83pub mod markers;
84pub mod projects;
85pub mod samples;
86#[cfg(test)]
87mod test_utils;
88
89use serde::{Deserialize, Serialize};
90use serde_big_array::Array;
91use std::array::from_fn;
92use std::{
93 error::Error,
94 fmt::Debug,
95 fs::File,
96 io::{Read, Write},
97 path::Path,
98};
99
100// todo: sized errors so not necessary to keep Boxing error enum varients
101/// Shorthand type alias for a Result with a Boxed Error
102type RBoxErr<T> = Result<T, Box<dyn Error>>;
103
104/// Global error variants
105#[derive(Debug, PartialEq, Eq)]
106pub enum OtToolsIoErrors {
107 /// An 'Options' Enum (e.g. `SampleAttributesLoopMode`) does not have a matching variant for this value
108 NoMatchingOptionEnumValue,
109 /// Could not parse a sample slot string data when loading a project
110 ProjectSampleSlotParsingError,
111 /// A file does not exist at the provided location
112 FileNotFound,
113 /// I know an error exists here, but I'm busy yak shaving something else at the moment.
114 TodoError,
115}
116impl std::fmt::Display for OtToolsIoErrors {
117 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
118 match self {
119 Self::NoMatchingOptionEnumValue => {
120 write!(f, "no matching enum option for the provided value")
121 }
122 Self::ProjectSampleSlotParsingError => {
123 write!(f, "count not load sample slot from project string data")
124 }
125 Self::FileNotFound => {
126 write!(f, "file not found")
127 }
128 Self::TodoError => {
129 write!(
130 f,
131 "this error is handled, but an error variant is not created yet"
132 )
133 }
134 }
135 }
136}
137impl Error for OtToolsIoErrors {}
138
139// DO-NOT-DERIVE: Implementation details for each enum are always required.
140/// Trait to convert between Enum option instances and their corresponding value.
141trait OptionEnumValueConvert {
142 /// One of the enum types within the `octatrack::options` module.
143 type T;
144
145 /// Input type for `from_value` and return type for `value` method.
146 type V;
147
148 /// Get an Enum instance from a numeric value.
149 fn from_value(v: &Self::V) -> RBoxErr<Self::T>;
150
151 /// Get a numeric value for an Enum instance.
152 fn value(&self) -> RBoxErr<Self::V>;
153}
154
155fn u8_bytes_to_u16(bytes: &[u8; 2]) -> u16 {
156 ((bytes[0] as u16) << 8) | bytes[1] as u16
157}
158
159/// Adds deserialisation via `bincode` and `serde` to a type.
160/// Must be present on all major file types.
161pub trait Decode {
162 fn decode(bytes: &[u8]) -> RBoxErr<Self>
163 where
164 Self: Sized,
165 Self: for<'a> Deserialize<'a>,
166 {
167 let x: Self = bincode::deserialize(bytes)?;
168 Ok(x)
169 }
170}
171
172/// Adds serialisation via `bincode` and `serde` to a type.
173/// Must be present on all major file types.
174pub trait Encode {
175 fn encode(&self) -> RBoxErr<Vec<u8>>
176 where
177 Self: Serialize,
178 {
179 Ok(bincode::serialize(&self)?)
180 }
181}
182
183/// Used when we need a collection of default type instances
184/// e.g. when creating a default bank we need 16 default patterns.
185/// ```
186/// // usage example
187///
188/// use ot_tools_io::DefaultsArray;
189///
190/// struct SomeType {
191/// x: u8,
192/// }
193///
194/// impl Default for SomeType {
195/// fn default() -> Self {
196/// Self { x: 0 }
197/// }
198/// }
199///
200/// impl DefaultsArray for SomeType {}
201///
202/// let xs: [SomeType; 20] = SomeType::defaults();
203/// assert_eq!(xs.len(), 20);
204///
205/// let xs = SomeType::defaults::<25>();
206/// assert_eq!(xs.len(), 25);
207/// ```
208pub trait DefaultsArray {
209 /// Create an Array containing `N` default instances of `Self`.
210 fn defaults<const N: usize>() -> [Self; N]
211 where
212 Self: Default,
213 {
214 from_fn(|_| Self::default())
215 }
216}
217
218/// Same as `DefaultsArray`, but using a boxed [serde_big_array::Array] container
219/// type
220/// ```
221/// // usage example
222///
223/// use serde_big_array::Array;
224/// use ot_tools_io::DefaultsArrayBoxed;
225///
226/// struct SomeType {
227/// x: u8,
228/// }
229///
230/// impl Default for SomeType {
231/// fn default() -> Self {
232/// Self { x: 0 }
233/// }
234/// }
235///
236/// impl DefaultsArrayBoxed for SomeType {}
237///
238/// let xs: Box<Array<SomeType, 20>> = SomeType::defaults();
239/// assert_eq!(xs.len(), 20);
240///
241/// let xs = SomeType::defaults::<25>();
242/// assert_eq!(xs.len(), 25);
243/// ```
244pub trait DefaultsArrayBoxed {
245 /// Create a Boxed [serde_big_array::Array] containing `N` default instances
246 /// of `Self`.
247 fn defaults<const N: usize>() -> Box<Array<Self, N>>
248 where
249 Self: Default,
250 {
251 Box::new(Array(from_fn(|_| Self::default())))
252 }
253}
254
255/// Adds a method to check the current data structure matches the default for the type
256/// ```rust
257/// use ot_tools_io::{IsDefault, banks::BankFile};
258///
259/// let mut bank = BankFile::default();
260/// assert_eq!(bank.is_default(), true);
261///
262/// bank.datatype_version = 190;
263/// assert_eq!(bank.is_default(), false);
264/// ```
265pub trait IsDefault {
266 fn is_default(&self) -> bool;
267}
268
269/// Method for calculating the checksum value for types that have a checksum field
270/// ```rust
271/// # use std::path::PathBuf;
272/// use ot_tools_io::{CalculateChecksum, read_type_from_bin_file, banks::BankFile};
273/// # let fpath = PathBuf::from("test-data")
274/// # .join("blank-project")
275/// # .join("bank01.work");
276/// let bank: BankFile = read_type_from_bin_file(&fpath).unwrap();
277/// assert_eq!(bank.checksum, bank.calculate_checksum().unwrap())
278/// ```
279pub trait CalculateChecksum {
280 fn calculate_checksum(&self) -> RBoxErr<u16>;
281}
282
283/// Adds a method to verify if header(s) are valid in some data.
284/// See this thread to understand why this is useful:
285/// <https://www.elektronauts.com/t/bank-unavailable-octatrack/190647/27>
286/// ```rust
287/// # use std::path::PathBuf;
288/// use ot_tools_io::{CheckHeader, read_type_from_bin_file, banks::BankFile};
289/// # let fpath = PathBuf::from("test-data")
290/// # .join("blank-project")
291/// # .join("bank01.work");
292/// let bank: BankFile = read_type_from_bin_file(&fpath).unwrap();
293/// assert!(bank.check_header()) // true for valid header values
294/// ```
295// NOTE: ot-tools-io does not validate headers on file read, which means it is
296// possible to perform checks like this when a data file has been read.
297// otherwise we'd have to do a complicated check to verify headers on every
298// file we read, then throw out an error and probably do some complicated
299// error handling to explain to the end user exactly why we couldn't load the
300// file (bad header, which patterns, which track within patterns etc.).
301pub trait CheckHeader {
302 fn check_header(&self) -> bool;
303}
304
305/// Adds a method to verify if checksum is valid in some data type.
306/// See this thread to understand why this is useful:
307/// <https://www.elektronauts.com/t/bank-unavailable-octatrack/190647/27>
308/// ```rust
309/// # use std::path::PathBuf;
310/// use ot_tools_io::{CheckChecksum, read_type_from_bin_file, banks::BankFile};
311/// # let fpath = PathBuf::from("test-data")
312/// # .join("blank-project")
313/// # .join("bank01.work");
314/// let bank: BankFile = read_type_from_bin_file(&fpath).unwrap();
315/// assert!(bank.check_checksum().unwrap()) // true for valid checksum values
316/// ```
317pub trait CheckChecksum {
318 fn check_checksum(&self) -> RBoxErr<bool>;
319}
320
321/// Adds a single method using the [crate::CheckHeader] and [crate::CheckChecksum]
322/// methods to run a full integrity check.
323/// ```rust
324/// # use std::path::PathBuf;
325/// use ot_tools_io::{CheckIntegrity, read_type_from_bin_file, banks::BankFile};
326/// # let fpath = PathBuf::from("test-data")
327/// # .join("blank-project")
328/// # .join("bank01.work");
329/// let bank: BankFile = read_type_from_bin_file(&fpath).unwrap();
330/// assert!(bank.check_integrity().unwrap()) // true for valid checksum+header values
331/// ```
332pub trait CheckIntegrity: CheckHeader + CheckChecksum {
333 fn check_integrity(&self) -> RBoxErr<bool> {
334 Ok(self.check_header() && self.check_checksum()?)
335 }
336}
337
338/* SER/DE GENERICS ============================================================================== */
339
340/// Deserialize a bytes to a data structure of type `T`
341/// ```rust
342/// # use std::array::from_fn;
343/// # use ot_tools_io::{deserialize_bin_to_type, samples::SampleAttributes};
344/// let bytes: [u8; 832] = from_fn(|_| 0);
345/// let samples_type: SampleAttributes = deserialize_bin_to_type(&bytes).unwrap();
346/// assert_eq!(samples_type.gain, 0)
347/// ```
348pub fn deserialize_bin_to_type<T>(bytes: &[u8]) -> RBoxErr<T>
349where
350 T: Decode,
351 T: for<'a> Deserialize<'a>,
352{
353 let x: T = T::decode(bytes)?;
354 Ok(x)
355}
356
357/// Serialize to bytes from a data structure of type `T`
358/// ```rust
359/// # use std::array::from_fn;
360/// # use ot_tools_io::{serialize_bin_from_type, banks::BankFile};
361/// let bank = BankFile::default();
362/// let bytes = serialize_bin_from_type::<BankFile>(&bank).unwrap();
363/// assert_eq!(bytes.len(), 636113);
364/// ```
365pub fn serialize_bin_from_type<T>(data: &T) -> RBoxErr<Vec<u8>>
366where
367 T: Encode,
368 T: Serialize,
369{
370 data.encode()
371}
372
373/// Deserialize a JSON string to a data structure of type `T`
374pub fn deserialize_json_to_type<T>(data: &str) -> RBoxErr<T>
375where
376 T: for<'a> Deserialize<'a>,
377{
378 let x: T = serde_json::from_str(data)?;
379 Ok(x)
380}
381
382/// Serialize a JSON string from a data structure of type `T`
383/// ```rust
384/// # use ot_tools_io::{serialize_json_from_type, banks::BankFile};
385/// let bank = BankFile::default();
386/// let json_string = serialize_json_from_type::<BankFile>(&bank).unwrap();
387/// assert_eq!(json_string.len(), 7807730);
388/// assert_eq!(json_string[0..15], "{\"header\":[70,7".to_string());
389/// ```
390pub fn serialize_json_from_type<T>(data: &T) -> RBoxErr<String>
391where
392 T: Serialize,
393{
394 Ok(serde_json::to_string(&data)?)
395}
396
397/// Deserialize a YAML string to a data structure of type `T`
398pub fn deserialize_yaml_to_type<T>(data: &str) -> RBoxErr<T>
399where
400 T: for<'a> Deserialize<'a>,
401{
402 let x: T = serde_yml::from_str(data)?;
403 Ok(x)
404}
405/// Serialize a YAML string from a data structure of type `T`
406/// ```rust
407/// # use ot_tools_io::{serialize_yaml_from_type, banks::BankFile};
408/// let bank = BankFile::default();
409/// let yaml_string = serialize_yaml_from_type::<BankFile>(&bank).unwrap();
410/// assert_eq!(yaml_string.len(), 12578394);
411/// assert_eq!(yaml_string[0..15], "header:\n- 70\n- ".to_string());
412/// ```
413pub fn serialize_yaml_from_type<T>(data: &T) -> RBoxErr<String>
414where
415 T: Serialize,
416{
417 Ok(serde_yml::to_string(&data)?)
418}
419
420/* UTILS ======================================================================================== */
421
422/* NO TESTS BLOCK START */
423
424/// Read a YAML file into a data structure of type `<T>`
425/// ```rust
426/// # use std::path::PathBuf;
427/// use ot_tools_io::{yaml_file_to_type, banks::BankFile};
428/// let fpath = PathBuf::from("test-data").join("bank").join("blank.yaml");
429/// let bank: BankFile = yaml_file_to_type(&fpath).unwrap();
430/// assert_eq!(bank.datatype_version, 23);
431/// ```
432pub fn yaml_file_to_type<T>(path: &Path) -> RBoxErr<T>
433where
434 T: for<'a> Deserialize<'a>,
435{
436 let string = read_str_file(path)?;
437 let data = deserialize_yaml_to_type::<T>(&string)?;
438 Ok(data)
439}
440
441/// Write a data structure of type `<T>` into a YAML file
442/// ```rust
443/// use std::env::temp_dir;
444/// # use std::fs::create_dir_all;
445/// use ot_tools_io::{type_to_yaml_file, banks::BankFile};
446///
447/// let fpath = temp_dir()
448/// .join("ot-tools-io")
449/// .join("doctest")
450/// .join("type_to_yaml_file.yaml");
451/// # create_dir_all(&fpath.parent().unwrap()).unwrap();
452///
453/// let r = type_to_yaml_file(&BankFile::default(), &fpath);
454/// assert!(r.is_ok());
455/// assert!(fpath.exists());
456/// ```
457pub fn type_to_yaml_file<T>(data: &T, path: &Path) -> RBoxErr<()>
458where
459 T: Serialize,
460{
461 let yaml = serialize_yaml_from_type::<T>(data)?;
462 write_str_file(&yaml, path)?;
463 Ok(())
464}
465
466/// Read a JSON file into a data structure of type `<T>`
467pub fn json_file_to_type<T>(path: &Path) -> RBoxErr<T>
468where
469 T: for<'a> Deserialize<'a>,
470{
471 let string = read_str_file(path)?;
472 let data = deserialize_json_to_type::<T>(&string)?;
473 Ok(data)
474}
475
476/// Write a data structure of type `<T>` into a JSON file
477/// ```rust
478/// use std::env::temp_dir;
479/// # use std::fs::create_dir_all;
480/// use ot_tools_io::{type_to_json_file, banks::BankFile};
481///
482/// let fpath = temp_dir()
483/// .join("ot-tools-io")
484/// .join("doctest")
485/// .join("type_to_json_file.json");
486/// # create_dir_all(&fpath.parent().unwrap()).unwrap();
487///
488/// let r = type_to_json_file(&BankFile::default(), &fpath);
489/// assert!(r.is_ok());
490/// assert!(fpath.exists());
491/// ```
492pub fn type_to_json_file<T>(data: &T, path: &Path) -> RBoxErr<()>
493where
494 T: Serialize,
495{
496 let yaml = serialize_json_from_type::<T>(data)?;
497 write_str_file(&yaml, path)?;
498 Ok(())
499}
500
501/// Create a new type with default settings, and write the data to a file.
502/// ```rust
503/// use std::env::temp_dir;
504/// # use std::fs::create_dir_all;
505/// use ot_tools_io::{default_type_to_bin_file, banks::BankFile};
506///
507/// let fpath = temp_dir()
508/// .join("ot-tools-io")
509/// .join("doctest")
510/// .join("default_type_to_bin_file.bank");
511/// # create_dir_all(&fpath.parent().unwrap()).unwrap();
512///
513/// let r = default_type_to_bin_file::<BankFile>(&fpath);
514/// assert!(r.is_ok());
515/// assert!(fpath.exists());
516/// ```
517pub fn default_type_to_bin_file<T>(outpath: &Path) -> RBoxErr<()>
518where
519 T: Encode,
520 T: Default,
521 T: Serialize,
522{
523 write_type_to_bin_file(&T::default(), outpath)?;
524 Ok(())
525}
526
527/* NO TESTS BLOCK ENDS */
528
529/// Show deserialized representation of a binary data file of type `T` at `path`
530pub fn show_type<T>(path: &Path, newlines: Option<bool>) -> RBoxErr<()>
531where
532 T: Debug,
533 T: Decode,
534 T: for<'a> Deserialize<'a>,
535{
536 let data = read_type_from_bin_file::<T>(path)?;
537 if newlines.unwrap_or(true) {
538 println!("{data:#?}")
539 } else {
540 println!("{data:?}")
541 };
542
543 Ok(())
544}
545
546/// Read a YAML file then write the data to a new `<T>` type file
547/// ```rust
548/// # use std::{path::PathBuf, fs::create_dir_all};
549/// use std::env::temp_dir;
550/// use ot_tools_io::{yaml_file_to_bin_file, banks::BankFile};
551///
552/// let yaml_fpath = PathBuf::from("test-data")
553/// .join("bank")
554/// .join("blank.yaml");
555///
556/// let bin_fpath = temp_dir()
557/// .join("ot-tools-io")
558/// .join("doctest")
559/// .join("yaml_to_bin.bank_example");
560/// # create_dir_all(&bin_fpath.parent().unwrap()).unwrap();
561///
562/// let r = yaml_file_to_bin_file::<BankFile>(&yaml_fpath, &bin_fpath);
563/// assert!(r.is_ok());
564/// assert!(bin_fpath.exists());
565/// ```
566pub fn yaml_file_to_bin_file<T>(yaml_filepath: &Path, bin_filepath: &Path) -> RBoxErr<()>
567where
568 T: Encode,
569 T: Serialize,
570 T: for<'a> Deserialize<'a>,
571{
572 let yaml = read_str_file(yaml_filepath)?;
573 let data = deserialize_yaml_to_type::<T>(&yaml)?;
574 write_type_to_bin_file::<T>(&data, bin_filepath)?;
575 Ok(())
576}
577
578/// Read data of type `<T>` from a binary data file and write it to a YAML file
579/// ```rust
580/// # use std::{path::PathBuf, fs::create_dir_all};
581/// use std::env::temp_dir;
582/// use ot_tools_io::{bin_file_to_yaml_file, banks::BankFile};
583///
584/// let bin_fpath = PathBuf::from("test-data")
585/// .join("blank-project")
586/// .join("bank01.work");
587///
588/// let yaml_fpath = temp_dir()
589/// .join("ot-tools-io")
590/// .join("doctest")
591/// .join("bin_to_yaml.bank_example");
592/// # create_dir_all(&yaml_fpath.parent().unwrap()).unwrap();
593///
594/// let r = bin_file_to_yaml_file::<BankFile>(&bin_fpath, &yaml_fpath);
595/// assert!(r.is_ok());
596/// assert!(bin_fpath.exists());
597/// ```
598pub fn bin_file_to_yaml_file<T>(bin_filepath: &Path, yaml_filepath: &Path) -> RBoxErr<()>
599where
600 T: Decode,
601 T: Serialize,
602 T: for<'a> Deserialize<'a>,
603{
604 let data = read_type_from_bin_file::<T>(bin_filepath)?;
605 let yaml = serialize_yaml_from_type::<T>(&data)?;
606 write_str_file(&yaml, yaml_filepath)?;
607 Ok(())
608}
609
610/// Read a JSON file then write the data to a new `<T>` type file
611pub fn json_file_to_bin_file<T>(json_filepath: &Path, bin_filepath: &Path) -> RBoxErr<()>
612where
613 T: Encode,
614 T: Serialize,
615 T: for<'a> Deserialize<'a>,
616{
617 let json = read_str_file(json_filepath)?;
618 let data = deserialize_json_to_type::<T>(&json)?;
619 write_type_to_bin_file::<T>(&data, bin_filepath)?;
620 Ok(())
621}
622
623/// Read data of type `<T>` from a binary data file and write it to a JSON file
624/// ```rust
625/// # use std::{path::PathBuf, fs::create_dir_all};
626/// use std::env::temp_dir;
627/// use ot_tools_io::{bin_file_to_json_file, banks::BankFile};
628///
629/// let bin_fpath = PathBuf::from("test-data")
630/// .join("blank-project")
631/// .join("bank01.work");
632///
633/// let json_fpath = temp_dir()
634/// .join("ot-tools-io")
635/// .join("doctest")
636/// .join("bin_to_json.bank_example");
637/// # create_dir_all(&json_fpath.parent().unwrap()).unwrap();
638///
639/// let r = bin_file_to_json_file::<BankFile>(&bin_fpath, &json_fpath);
640/// assert!(r.is_ok());
641/// assert!(bin_fpath.exists());
642/// ```
643pub fn bin_file_to_json_file<T>(bin_filepath: &Path, json_filepath: &Path) -> RBoxErr<()>
644where
645 T: Decode,
646 T: Serialize,
647 T: for<'a> Deserialize<'a>,
648{
649 let data = read_type_from_bin_file::<T>(bin_filepath)?;
650 let yaml = serialize_json_from_type::<T>(&data)?;
651 write_str_file(&yaml, json_filepath)?;
652 Ok(())
653}
654
655/// Read a data structure of type `<T>` from a binary data file.
656/// ```rust
657/// use std::path::PathBuf;
658/// use ot_tools_io::{read_type_from_bin_file, banks::BankFile};
659///
660/// let fpath = PathBuf::from("test-data")
661/// .join("blank-project")
662/// .join("bank01.work");
663///
664/// let bank: BankFile = read_type_from_bin_file(&fpath).unwrap();
665/// assert_eq!(bank.datatype_version, 23);
666/// ```
667pub fn read_type_from_bin_file<T>(path: &Path) -> RBoxErr<T>
668where
669 T: Decode,
670 T: for<'a> Deserialize<'a>,
671{
672 let bytes = read_bin_file(path)?;
673 let data = deserialize_bin_to_type::<T>(&bytes)?;
674 Ok(data)
675}
676
677/// Write a data structue of type `<T>` to a binary data file.
678/// ```rust
679/// # use std::{path::PathBuf, fs::create_dir_all};
680/// use std::env::temp_dir;
681/// use ot_tools_io::{write_type_to_bin_file, banks::BankFile};
682///
683/// let fpath = temp_dir()
684/// .join("ot-tools-io")
685/// .join("doctest")
686/// .join("write_type_to_bin_file.bank_example");
687/// # create_dir_all(&fpath.parent().unwrap()).unwrap();
688///
689/// let r = write_type_to_bin_file::<BankFile>(&BankFile::default(), &fpath);
690/// assert!(r.is_ok());
691/// assert!(fpath.exists());
692/// ```
693pub fn write_type_to_bin_file<T>(data: &T, path: &Path) -> RBoxErr<()>
694where
695 T: Encode,
696 T: Serialize,
697{
698 let bytes = serialize_bin_from_type::<T>(data)?;
699 write_bin_file(&bytes, path)?;
700 Ok(())
701}
702
703/// Read bytes from a file at `path`.
704/// ```rust
705/// let fpath = std::path::PathBuf::from("test-data")
706/// .join("blank-project")
707/// .join("bank01.work");
708/// let r = ot_tools_io::read_bin_file(&fpath);
709/// assert!(r.is_ok());
710/// assert_eq!(r.unwrap().len(), 636113);
711pub fn read_bin_file(path: &Path) -> RBoxErr<Vec<u8>> {
712 let mut infile = File::open(path)?;
713 let mut bytes: Vec<u8> = vec![];
714 let _: usize = infile.read_to_end(&mut bytes)?;
715 Ok(bytes)
716}
717
718/// Write bytes to a file at `path`.
719/// ```rust
720/// use std::env::temp_dir;
721/// use std::array::from_fn;
722///
723/// let arr: [u8; 27] = from_fn(|_| 0);
724///
725/// let fpath = temp_dir()
726/// .join("ot-tools-io")
727/// .join("doctest")
728/// .join("write_bin_file.example");
729///
730/// let r = ot_tools_io::write_bin_file(&arr, &fpath);
731/// assert!(r.is_ok());
732/// assert!(fpath.exists());
733///
734/// ```
735pub fn write_bin_file(bytes: &[u8], path: &Path) -> RBoxErr<()> {
736 let mut file: File = File::create(path)?;
737 file.write_all(bytes)?;
738 Ok(())
739}
740
741/// Read a file at `path` as a string.
742pub fn read_str_file(path: &Path) -> RBoxErr<String> {
743 let mut file = File::open(path)?;
744 let mut string = String::new();
745 let _ = file.read_to_string(&mut string)?;
746 Ok(string)
747}
748
749/// Write a string to a file at `path`.
750pub fn write_str_file(string: &str, path: &Path) -> RBoxErr<()> {
751 let mut file: File = File::create(path)?;
752 write!(file, "{}", string)?;
753 Ok(())
754}
755
756#[cfg(test)]
757#[allow(unused_imports)]
758mod test {
759 use super::*;
760 use crate::arrangements::ArrangementFile;
761 use crate::banks::BankFile;
762 use crate::projects::ProjectFile;
763 use crate::samples::SampleAttributes;
764 use crate::test_utils::*;
765 mod show_ok {
766 use super::*;
767 #[test]
768 fn test_arrangement() {
769 let fp = get_blank_proj_dirpath().join("arr01.work");
770 let r = show_type::<ArrangementFile>(&fp, None);
771 assert!(r.is_ok())
772 }
773
774 #[test]
775 fn test_bank() {
776 let fp = get_blank_proj_dirpath().join("bank01.work");
777 let r = show_type::<BankFile>(&fp, None);
778 assert!(r.is_ok())
779 }
780
781 #[test]
782 fn test_project() {
783 let fp = get_blank_proj_dirpath().join("project.work");
784 let r = show_type::<ProjectFile>(&fp, None);
785 assert!(r.is_ok())
786 }
787
788 #[test]
789 fn test_sample() {
790 let fp = get_samples_dirpath().join("sample.ot");
791 let r = show_type::<SampleAttributes>(&fp, None);
792 assert!(r.is_ok())
793 }
794 }
795
796 // TODO: Add more cases
797 mod yaml_file_to_bin_file_ok {
798 use super::*;
799 use crate::arrangements::ArrangeRow;
800
801 mod arrangement {
802 use crate::arrangements::ArrangementFile;
803 use crate::test_utils::get_arrange_dirpath;
804 use crate::{read_type_from_bin_file, yaml_file_to_bin_file};
805
806 fn helper_arrangement(fname: String) {
807 let testfile = get_arrange_dirpath().join(format!["{fname}.work"]);
808 let outfile = std::env::temp_dir()
809 .join(format!["ot-tools-io-arrangement-load-test-{fname}.work"]);
810 let yaml = get_arrange_dirpath().join(format!["{fname}.yaml"]);
811 let r = yaml_file_to_bin_file::<ArrangementFile>(&yaml, &outfile);
812 let written = read_type_from_bin_file::<ArrangementFile>(&outfile).unwrap();
813 let valid = read_type_from_bin_file::<ArrangementFile>(&testfile).unwrap();
814 let _ = std::fs::remove_file(&outfile);
815 println!("{r:?}");
816 assert!(r.is_ok());
817 assert_eq!(written, valid);
818 }
819 #[test]
820 fn blank() {
821 helper_arrangement("blank".to_string());
822 }
823
824 #[test]
825 fn full_options() {
826 helper_arrangement("full-options".to_string());
827 }
828
829 #[test]
830 fn full_options_no_rems() {
831 helper_arrangement("full-options-no-rems".to_string());
832 }
833
834 #[test]
835 fn no_saved_flag() {
836 helper_arrangement("no-saved-flag".to_string());
837 }
838
839 #[test]
840 fn with_saved_flag() {
841 helper_arrangement("with-saved-flag".to_string());
842 }
843
844 #[test]
845 fn blank_diffname_1() {
846 helper_arrangement("blank-diffname1".to_string());
847 }
848
849 #[test]
850 fn blank_diffname_2() {
851 helper_arrangement("blank-diffname2".to_string());
852 }
853
854 #[test]
855 fn four_patterns() {
856 helper_arrangement("4-patterns".to_string());
857 }
858
859 #[test]
860 fn one_pattern() {
861 helper_arrangement("1-pattern".to_string());
862 }
863 #[test]
864 fn one_halt() {
865 helper_arrangement("1-halt".to_string());
866 }
867 #[test]
868 fn one_pattern_1_loop() {
869 helper_arrangement("1-patt-1-loop".to_string());
870 }
871 #[test]
872 fn one_pattern_1_loop_1_jump() {
873 helper_arrangement("1-patt-1-jump-1-loop".to_string());
874 }
875 #[test]
876 fn one_pattern_1_loop_1_jump_1_halt() {
877 helper_arrangement("1-patt-1-jump-1-loop-1-halt".to_string());
878 }
879 #[test]
880 fn one_rems_no_txt() {
881 helper_arrangement("1-rem-blank-txt".to_string());
882 }
883 #[test]
884 fn one_rems_chain_txt() {
885 helper_arrangement("1-rem-CHAIN-txt".to_string());
886 }
887 #[test]
888 fn two_rems_chain_txt() {
889 helper_arrangement("2-rem-CHAIN-txt".to_string());
890 }
891 #[test]
892 fn blank_same_name_saved() {
893 helper_arrangement("blank-samename-saved-chktest".to_string());
894 }
895 #[test]
896 fn blank_same_name_unsaved() {
897 helper_arrangement("blank-samename-unsaved-chktest".to_string());
898 }
899 }
900
901 #[test]
902 fn test_project() {
903 let outfile = std::env::temp_dir().join("ot-tools-actions-project-load-test-ok.work");
904 let yaml = get_project_dirpath().join("project.yaml");
905 let r = yaml_file_to_bin_file::<ProjectFile>(&yaml, &outfile);
906 let _ = std::fs::remove_file(&outfile);
907 println!("{r:?}");
908 assert!(r.is_ok())
909 }
910
911 #[test]
912 fn test_project_matches_blank() {
913 let testfile = get_project_dirpath().join("blank.work");
914 let outfile = std::env::temp_dir().join("ot-tools-actions-project-load-test-full.work");
915 let yaml = get_project_dirpath().join("project.yaml");
916
917 let r = yaml_file_to_bin_file::<ProjectFile>(&yaml, &outfile);
918
919 let written = read_type_from_bin_file::<ProjectFile>(&outfile).unwrap();
920 let valid = read_type_from_bin_file::<ProjectFile>(&testfile).unwrap();
921
922 let _ = std::fs::remove_file(&outfile);
923 println!("{r:?}");
924 assert!(r.is_ok());
925 assert_eq!(written, valid)
926 }
927
928 #[test]
929 fn test_bank() {
930 let outfile = std::env::temp_dir().join("ot-tools-actions-bank-load-test-ok.work");
931 let yaml = get_bank_dirpath().join("blank.yaml");
932 let r = yaml_file_to_bin_file::<BankFile>(&yaml, &outfile);
933 let _ = std::fs::remove_file(&outfile);
934 println!("{r:?}");
935 assert!(r.is_ok())
936 }
937
938 #[test]
939 fn test_sample() {
940 let outfile = std::env::temp_dir().join("ot-tools-actions-sample-load-test-ok.work");
941 let yaml = get_samples_dirpath().join("chain.yaml");
942 let r = yaml_file_to_bin_file::<SampleAttributes>(&yaml, &outfile);
943 let _ = std::fs::remove_file(&outfile);
944 println!("{r:?}");
945 assert!(r.is_ok());
946 }
947 }
948
949 mod bin_file_to_yaml_file_ok {
950 use super::*;
951 #[test]
952 // Windows will add on carriage returns...
953 #[cfg(not(target_os = "windows"))]
954 fn arrangement_blank() {
955 let valid_yaml_path = get_arrange_dirpath().join("blank.yaml");
956 let binpath = get_arrange_dirpath().join("blank.work");
957 let outyaml = std::env::temp_dir().join("serde-ot-bin2yaml-arrange-blank.yaml");
958
959 let r = crate::bin_file_to_yaml_file::<super::ArrangementFile>(&binpath, &outyaml);
960 let written = crate::read_str_file(&outyaml).unwrap();
961 let valid = crate::read_str_file(&valid_yaml_path).unwrap();
962
963 let _ = std::fs::remove_file(&outyaml);
964 println!("{r:?}");
965 assert!(r.is_ok());
966 assert_eq!(valid, written);
967 }
968
969 #[test]
970 // Windows will add on carriage returns...
971 #[cfg(not(target_os = "windows"))]
972 fn arrangement_full_options() {
973 let valid_yaml_path = get_arrange_dirpath().join("full-options.yaml");
974 let binpath = get_arrange_dirpath().join("full-options.work");
975 let outyaml = std::env::temp_dir().join("serde-ot-bin2yaml-arrange-fulloptions.yaml");
976
977 let r = crate::bin_file_to_yaml_file::<super::ArrangementFile>(&binpath, &outyaml);
978 let written = crate::read_str_file(&outyaml).unwrap();
979 let valid = crate::read_str_file(&valid_yaml_path).unwrap();
980
981 let _ = std::fs::remove_file(&outyaml);
982 println!("{r:?}");
983 assert!(r.is_ok());
984 assert_eq!(valid, written);
985 }
986
987 #[test]
988 // Windows will add on carriage returns...
989 #[cfg(not(target_os = "windows"))]
990 fn arrangement_one_blank_reminder_row() {
991 let valid_yaml_path = get_arrange_dirpath().join("1-rem-blank-txt.yaml");
992 let binpath = get_arrange_dirpath().join("1-rem-blank-txt.work");
993 let outyaml =
994 std::env::temp_dir().join("serde-ot-bin2yaml-arrange-onereminderrow.yaml");
995
996 let r = crate::bin_file_to_yaml_file::<super::ArrangementFile>(&binpath, &outyaml);
997 let written = crate::read_str_file(&outyaml).unwrap();
998 let valid = crate::read_str_file(&valid_yaml_path).unwrap();
999
1000 let _ = std::fs::remove_file(&outyaml);
1001 println!("{r:?}");
1002 assert!(r.is_ok());
1003 assert_eq!(valid, written);
1004 }
1005
1006 #[test]
1007 fn test_bank() {
1008 let outfile = std::env::temp_dir().join("ot-tools-actions-bin2yaml-bank-ok.yaml");
1009 let binfile = get_blank_proj_dirpath().join("bank01.work");
1010 let r = bin_file_to_yaml_file::<BankFile>(&binfile, &outfile);
1011 let _ = std::fs::remove_file(&outfile);
1012 assert!(r.is_ok())
1013 }
1014
1015 #[test]
1016 fn test_project() {
1017 let outfile = std::env::temp_dir().join("ot-tools-actions-bin2yaml-project-ok.yaml");
1018 let binfile = get_blank_proj_dirpath().join("project.work");
1019 let r = bin_file_to_yaml_file::<ProjectFile>(&binfile, &outfile);
1020 let _ = std::fs::remove_file(&outfile);
1021 assert!(r.is_ok())
1022 }
1023
1024 #[test]
1025 fn test_sample() {
1026 let outfile = std::env::temp_dir().join("ot-tools-actions-bin2yaml-sample-ok.yaml");
1027 let binfile = get_samples_dirpath().join("sample.ot");
1028 let r = bin_file_to_yaml_file::<SampleAttributes>(&binfile, &outfile);
1029 let _ = std::fs::remove_file(&outfile);
1030 assert!(r.is_ok())
1031 }
1032 }
1033
1034 mod bin_file_to_json_file_ok {
1035 use super::*;
1036
1037 #[test]
1038 fn arrangement_blank() {
1039 // let valid_json_path = std::path::Path::new("TODO");
1040
1041 let binpath = get_arrange_dirpath().join("blank.work");
1042 let outjson = std::env::temp_dir().join("serde-ot-bin2yaml-arrange-blank.json");
1043
1044 let r = crate::bin_file_to_json_file::<super::ArrangementFile>(&binpath, &outjson);
1045 let written = crate::read_str_file(&outjson);
1046 // let valid = crate::read_str_file(&valid_json_path).unwrap();
1047
1048 let _ = std::fs::remove_file(&outjson);
1049 println!("{r:?}");
1050 assert!(r.is_ok());
1051 assert!(written.is_ok());
1052 // assert_eq!(valid, written);
1053 }
1054
1055 #[test]
1056 fn arrangement_full_options() {
1057 // let valid_json_path = std::path::Path::new("TODO");
1058 let binpath = get_arrange_dirpath().join("full-options.work");
1059 let outjson = std::env::temp_dir().join("serde-ot-bin2yaml-arrange-full.json");
1060
1061 let r = crate::bin_file_to_json_file::<super::ArrangementFile>(&binpath, &outjson);
1062 let written = crate::read_str_file(&outjson);
1063 // let valid = crate::read_str_file(&valid_json_path).unwrap();
1064
1065 let _ = std::fs::remove_file(&outjson);
1066 println!("{r:?}");
1067 assert!(r.is_ok());
1068 assert!(written.is_ok());
1069 // assert_eq!(valid, written);
1070 }
1071
1072 #[test]
1073 fn arrangement_one_reminder_row() {
1074 // let valid_json_path = std::path::Path::new("TODO");
1075 let binpath = get_arrange_dirpath().join("1-rem-blank-txt.work");
1076 let outjson =
1077 std::env::temp_dir().join("serde-ot-bin2yaml-arrange-one_rem_row_blank.json");
1078
1079 let r = crate::bin_file_to_json_file::<super::ArrangementFile>(&binpath, &outjson);
1080 let written = crate::read_str_file(&outjson);
1081 // let valid = crate::read_str_file(&valid_json_path).unwrap();
1082
1083 let _ = std::fs::remove_file(&outjson);
1084 println!("{r:?}");
1085 assert!(r.is_ok());
1086 assert!(written.is_ok());
1087 // assert_eq!(valid, written);
1088 }
1089
1090 #[test]
1091 fn test_bank() {
1092 let outfile = std::env::temp_dir().join("ot-tools-actions-bin2yaml-bank-ok.json");
1093 let binfile = get_blank_proj_dirpath().join("bank01.work");
1094 let r = bin_file_to_json_file::<BankFile>(&binfile, &outfile);
1095 let _ = std::fs::remove_file(&outfile);
1096 assert!(r.is_ok())
1097 }
1098
1099 #[test]
1100 fn test_project() {
1101 let outfile = std::env::temp_dir().join("ot-tools-actions-bin2yaml-project-ok.json");
1102 let binfile = get_blank_proj_dirpath().join("project.work");
1103 let r = bin_file_to_json_file::<ProjectFile>(&binfile, &outfile);
1104 let _ = std::fs::remove_file(&outfile);
1105 assert!(r.is_ok())
1106 }
1107
1108 #[test]
1109 fn test_sample() {
1110 let outfile = std::env::temp_dir().join("ot-tools-actions-bin2yaml-sample-ok.json");
1111 let binfile = get_samples_dirpath().join("sample.ot");
1112 let r = bin_file_to_json_file::<SampleAttributes>(&binfile, &outfile);
1113 let _ = std::fs::remove_file(&outfile);
1114 assert!(r.is_ok())
1115 }
1116 }
1117
1118 // TODO: Add more cases!
1119 mod json_file_to_bin_file_ok {
1120 // #[test]
1121 // fn test_arrangement() {
1122 // let outfile = std::env::temp_dir().join("ot-tools-actions-arrangement-load-test-ok.work");
1123 // let yaml = PathBuf::from("TODO");
1124 // // TODO!
1125 // let r = yaml_file_to_bin_file::<ArrangementFile>(&yaml, &outfile);
1126 // let _ = std::fs::remove_file(&outfile);
1127 // assert!(r.is_ok())
1128 // }
1129
1130 // #[test]
1131 // fn test_bank() {
1132 // let outfile = std::env::temp_dir().join("ot-tools-actions-bank-load-test-ok.work");
1133 // let yaml = PathBuf::from("TODO");
1134 // let r = yaml_file_to_bin_file::<Bank>(&yaml, &outfile);
1135 // let _ = std::fs::remove_file(&outfile);
1136 // assert!(r.is_ok())
1137 // }
1138
1139 // #[test]
1140 // fn test_project() {
1141 // let outfile = std::env::temp_dir().join("ot-tools-actions-project-load-test-ok.work");
1142 // let yaml = PathBuf::from("../data/tests/projects/project.yaml");
1143 // let r = yaml_file_to_bin_file::<Project>(&yaml, &outfile);
1144 // let _ = std::fs::remove_file(&outfile);
1145 // assert!(r.is_ok())
1146 // }
1147
1148 // #[test]
1149 // fn test_sample() {
1150 // let outfile = std::env::temp_dir().join("ot-tools-actions-sample-load-test-ok.work");
1151 // let yaml = PathBuf::from("TODO");
1152 // let r = yaml_file_to_bin_file::<SampleAttributes>(&yaml, &outfile);
1153 // let _ = std::fs::remove_file(&outfile);
1154 // assert!(r.is_ok())
1155 // }
1156 }
1157
1158 mod read_type_from_bin_file_ok {
1159 use super::*;
1160
1161 #[test]
1162 fn arrangement_blank() {
1163 let binfile = get_arrange_dirpath().join("blank.work");
1164 let r = read_type_from_bin_file::<ArrangementFile>(&binfile);
1165 assert!(r.is_ok())
1166 }
1167
1168 #[test]
1169 fn arrangement_full_options() {
1170 let binfile = get_arrange_dirpath().join("full-options.work");
1171 let r = read_type_from_bin_file::<ArrangementFile>(&binfile);
1172 assert!(r.is_ok())
1173 }
1174
1175 #[test]
1176 fn arrangement_one_reminder() {
1177 let binfile = get_arrange_dirpath().join("1-rem-blank-txt.work");
1178 let r = read_type_from_bin_file::<ArrangementFile>(&binfile);
1179 assert!(r.is_ok())
1180 }
1181
1182 #[test]
1183 fn test_read_type_from_bin_file_bank() {
1184 let binfile = get_blank_proj_dirpath().join("bank01.work");
1185 let r = read_type_from_bin_file::<BankFile>(&binfile);
1186 assert!(r.is_ok())
1187 }
1188
1189 #[test]
1190 fn test_read_type_from_bin_file_project() {
1191 let binfile = get_blank_proj_dirpath().join("project.work");
1192 let r = read_type_from_bin_file::<ProjectFile>(&binfile);
1193 assert!(r.is_ok())
1194 }
1195
1196 #[test]
1197 fn test_read_type_from_bin_file_sample() {
1198 let binfile = get_samples_dirpath().join("sample.ot");
1199 let r = read_type_from_bin_file::<SampleAttributes>(&binfile);
1200 assert!(r.is_ok())
1201 }
1202 }
1203
1204 mod write_type_from_bin_file_ok {
1205 // TODO
1206 }
1207
1208 mod read_bin_file_ok {
1209 use super::*;
1210
1211 #[test]
1212 fn arrangement_blank() {
1213 let binfile = get_arrange_dirpath().join("blank.work");
1214 let r = read_bin_file(&binfile);
1215 assert!(r.is_ok())
1216 }
1217
1218 #[test]
1219 fn arrangement_full_options() {
1220 let binfile = get_arrange_dirpath().join("full-options.work");
1221 let r = read_bin_file(&binfile);
1222 assert!(r.is_ok())
1223 }
1224
1225 #[test]
1226 fn arrangement_one_reminder() {
1227 let binfile = get_arrange_dirpath().join("1-rem-blank-txt.work");
1228 let r = read_bin_file(&binfile);
1229 assert!(r.is_ok())
1230 }
1231
1232 #[test]
1233 fn test_read_bin_file_bank() {
1234 let binfile = get_blank_proj_dirpath().join("bank01.work");
1235 let r = read_bin_file(&binfile);
1236 assert!(r.is_ok())
1237 }
1238
1239 #[test]
1240 fn test_read_bin_file_project() {
1241 let binfile = get_blank_proj_dirpath().join("project.work");
1242 let r = read_bin_file(&binfile);
1243 assert!(r.is_ok())
1244 }
1245
1246 #[test]
1247 fn test_read_bin_file_sample() {
1248 let binfile = get_samples_dirpath().join("sample.ot");
1249 let r = read_bin_file(&binfile);
1250 assert!(r.is_ok())
1251 }
1252 }
1253
1254 mod write_bin_file_ok {
1255 // TODO
1256 }
1257
1258 mod read_str_file_ok {
1259 // TODO
1260 }
1261
1262 mod write_str_file_ok {
1263 // TODO
1264 }
1265
1266 // TODO: This probably shouldn't be here...
1267 mod project_read {
1268 use super::*;
1269 use crate::projects::metadata::ProjectMetadata;
1270 use crate::projects::settings::ProjectSettings;
1271 use crate::projects::slots::ProjectSampleSlot;
1272 use crate::projects::states::ProjectStates;
1273 use crate::projects::ProjectFile;
1274 // can read a project file without errors
1275 #[test]
1276 fn test_read_default_project_work_file() {
1277 let infile = get_blank_proj_dirpath().join("project.work");
1278 assert!(read_type_from_bin_file::<ProjectFile>(&infile).is_ok());
1279 }
1280
1281 // test that the metadata section is correct
1282 #[test]
1283 fn test_read_default_project_work_file_metadata() {
1284 let infile = get_blank_proj_dirpath().join("project.work");
1285 let p = read_type_from_bin_file::<ProjectFile>(&infile).unwrap();
1286
1287 let correct = ProjectMetadata::default();
1288
1289 assert_eq!(p.metadata, correct);
1290 }
1291
1292 // test that the states section is correct
1293 #[test]
1294 fn test_read_default_project_work_file_states() {
1295 let infile = get_blank_proj_dirpath().join("project.work");
1296 let p = read_type_from_bin_file::<ProjectFile>(&infile).unwrap();
1297
1298 let correct = ProjectStates::default();
1299
1300 assert_eq!(p.states, correct);
1301 }
1302
1303 // test that the states section is correct
1304 #[test]
1305 fn test_read_default_project_work_file_settings() {
1306 let infile = get_blank_proj_dirpath().join("project.work");
1307 let p = read_type_from_bin_file::<ProjectFile>(&infile).unwrap();
1308
1309 let correct = ProjectSettings::default();
1310
1311 assert_eq!(p.settings, correct);
1312 }
1313
1314 // test that the states section is correct
1315 #[test]
1316 fn test_read_default_project_work_file_sslots() {
1317 let infile = get_blank_proj_dirpath().join("project.work");
1318 let p = read_type_from_bin_file::<ProjectFile>(&infile).unwrap();
1319 let default_sslots = ProjectSampleSlot::defaults();
1320
1321 assert_eq!(p.slots, default_sslots);
1322 }
1323
1324 // test that reading and writing a single project gives the same outputs
1325 #[test]
1326 fn test_read_write_default_project_work_file() {
1327 let infile = get_blank_proj_dirpath().join("project.work");
1328 let outfile = std::env::temp_dir().join("default_1.work");
1329 let p = read_type_from_bin_file::<ProjectFile>(&infile).unwrap();
1330 let _ = write_type_to_bin_file::<ProjectFile>(&p, &outfile);
1331
1332 let p_reread = read_type_from_bin_file::<ProjectFile>(&infile).unwrap();
1333
1334 assert_eq!(p, p_reread)
1335 }
1336 }
1337}