pub mod arrangements;
pub mod banks;
pub mod errors;
mod generics;
pub mod identifiers;
mod macros;
pub mod markers;
pub mod parts;
pub mod patterns;
pub mod projects;
pub mod samples;
pub mod settings;
pub mod slices;
#[cfg(test)]
#[allow(dead_code)]
mod test_utils;
mod traits;
pub use crate::arrangements::ArrangementFile;
pub use crate::banks::BankFile;
pub use crate::markers::MarkersFile;
pub use crate::projects::ProjectFile;
pub use crate::samples::SampleSettingsFile;
pub use crate::traits::{
CheckFileIntegrity, Defaults, HasChecksumField, HasFileVersionField, HasHeaderField, IsDefault,
OctatrackFileIO,
};
use crate::markers::SlotMarkersError;
use crate::projects::{ProjectError, ProjectParseError, ProjectSlotsError};
use crate::samples::SampleSettingsError;
use crate::settings::InvalidValueError;
use crate::slices::SliceError;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum OtToolsIoError {
#[error("File OS Error: {source} Path={path} (std::io::Error)")]
FileOs {
#[source]
source: std::io::Error,
path: PathBuf,
},
#[error("File I/O Error: {0} (std::io::Error)")]
FileIo(#[from] std::io::Error),
#[error("error during binary data decoding / encoding: {0} (bincode::Error)")]
Bincode(#[from] bincode::Error),
#[error("error reading utf8 string data: {0} (std::str::Utf8Error)")]
ReadUtf8(#[from] std::str::Utf8Error),
#[error("error parsing raw project data: {0} (ot_tools_io::projects::ProjectParseError)")]
ProjectParse(#[from] ProjectParseError),
#[error("error parsing project slots data: {0} (ot_tools_io::projects::ProjectSlotsError)")]
ProjectSlots(#[from] ProjectSlotsError),
#[error("arrangement error: {0}")]
Arrangement(#[from] arrangements::ArrangementError),
#[error("error processing yaml: {0} (serde_norway::Error)")]
YamlParse(#[from] serde_norway::Error),
#[error("error processing json: {0} (serde_json::Error)")]
JsonParse(#[from] serde_json::Error),
#[error(
"project files cannot be checked for integrity: {0} (ot_tools_io::projects::ProjectError)"
)]
ProjectFile(#[from] ProjectError),
#[error("sample settings error: {0} (ot_tools_io::samples::SampleSettingsError)")]
SampleSettings(#[from] SampleSettingsError),
#[error("markers error: {0} (ot_tools_io::markers::MarkersErrors)")]
Markers(#[from] SlotMarkersError),
#[error("slices error: {0} (ot_tools_io::slices::SlicesErrors)")]
Slice(#[from] SliceError),
#[error("invalid setting value: {0}")]
SettingValue(#[from] InvalidValueError),
#[error("invalid header(s) for file")]
FileHeader,
#[error("invalid index for identifier")]
InvalidIndex,
}
pub mod types {
pub type SlotCombo = (SlotAttributes, SlotMarkers);
pub use crate::arrangements::ArrangeRow;
pub use crate::arrangements::ArrangementState;
pub use crate::arrangements::LoopOrJumpOrHaltRow;
pub use crate::arrangements::PatternRow;
pub use crate::arrangements::ReminderRow;
pub use crate::markers::SlotMarkers;
pub use crate::parts::Part;
pub use crate::patterns::Pattern;
pub use crate::projects::SlotAttributes;
pub use crate::settings::SlotType;
pub use crate::slices::Slice;
pub use crate::ArrangementFile;
pub use crate::BankFile;
pub use crate::MarkersFile;
pub use crate::ProjectFile;
pub use crate::SampleSettingsFile;
pub use crate::generics::ActiveSlot;
pub use crate::generics::ArrangeRows;
pub use crate::generics::Arrangements;
pub use crate::generics::Banks;
pub use crate::generics::Parts;
pub use crate::generics::Patterns;
pub use crate::generics::PlaybackSlots;
pub use crate::generics::RecordingBufferSlots;
pub use crate::generics::Scenes;
pub use crate::generics::Slices;
pub use crate::generics::Slots;
pub use crate::generics::Tracks;
pub use crate::generics::Trigs;
}
pub const ALLOWED_OS_VERSIONS: [&str; 3] = ["1.40A", "1.40B", "1.40C"];
#[doc(hidden)]
fn read_bin_file(path: &Path) -> Result<Vec<u8>, OtToolsIoError> {
let mut infile = File::open(path).map_err(|e| OtToolsIoError::FileOs {
path: path.to_path_buf(),
source: e,
})?;
let mut bytes: Vec<u8> = vec![];
let _: usize = infile.read_to_end(&mut bytes)?;
Ok(bytes)
}
#[cfg(test)]
mod read_bin_file {
use crate::test_utils::*;
use crate::{read_bin_file, OtToolsIoError};
#[test]
fn ok() -> Result<(), OtToolsIoError> {
let path = get_blank_proj_dirpath().join("bank01.work");
read_bin_file(&path)?;
Ok(())
}
#[test]
fn err_file_io() -> Result<(), OtToolsIoError> {
let path = get_blank_proj_dirpath().join("NOTNTONTONTNTOTNONT");
#[cfg(target_os = "windows")]
assert_eq!(
read_bin_file(&path).unwrap_err().to_string(),
format!["File OS Error: The system cannot find the file specified. (os error 2) Path={} (std::io::Error)", path.display()].to_string(),
"should throw a OtToolsIoError::FileOS error when file does not exist"
);
#[cfg(target_os = "linux")]
assert_eq!(
read_bin_file(&path).unwrap_err().to_string(),
format![
"File OS Error: No such file or directory (os error 2) Path={} (std::io::Error)",
path.display()
]
.to_string(),
"should throw a OtToolsIoError::FileOS error when file does not exist"
);
Ok(())
}
}
#[doc(hidden)]
fn write_bin_file(bytes: &[u8], path: &Path) -> Result<(), OtToolsIoError> {
let mut file: File = File::create(path).map_err(|e| OtToolsIoError::FileOs {
path: path.to_path_buf(),
source: e,
})?;
file.write_all(bytes)?;
Ok(())
}
#[cfg(test)]
mod write_bin_file {
use crate::{write_bin_file, OtToolsIoError};
use std::env::temp_dir;
use std::fs::{create_dir_all, remove_file};
#[test]
fn ok() -> Result<(), OtToolsIoError> {
let path = temp_dir()
.join("ot-tools-io")
.join("write_bin_file")
.join("ok.bin");
create_dir_all(path.parent().unwrap())?;
if path.exists() {
remove_file(&path)?;
};
write_bin_file(&[1, 2, 3, 4], &path)?;
Ok(())
}
#[test]
fn err_file_io() -> Result<(), OtToolsIoError> {
let path = temp_dir().join("ot-tools-io").join("write_bin_file");
create_dir_all(&path)?;
#[cfg(target_os = "windows")]
assert_eq!(
write_bin_file(&[1, 2, 3, 4], &path)
.unwrap_err()
.to_string(),
format![
"File OS Error: Access is denied. (os error 5) Path={} (std::io::Error)",
path.display()
]
.to_string(),
"should throw a OtToolsIoError::FileOS error when a file cannot be created"
);
#[cfg(target_os = "linux")]
assert_eq!(
write_bin_file(&[1, 2, 3, 4], &path)
.unwrap_err()
.to_string(),
format![
"File OS Error: Is a directory (os error 21) Path={} (std::io::Error)",
path.display()
]
.to_string(),
"should throw a OtToolsIoError::FileOS error when a file cannot be created"
);
Ok(())
}
}
#[doc(hidden)]
fn read_str_file(path: &Path) -> Result<String, OtToolsIoError> {
let mut file = File::open(path).map_err(|e| OtToolsIoError::FileOs {
path: path.to_path_buf(),
source: e,
})?;
let mut string = String::new();
let _ = file.read_to_string(&mut string)?;
Ok(string)
}
#[cfg(test)]
mod read_str_file {
use crate::test_utils::*;
use crate::{read_str_file, OtToolsIoError};
#[test]
fn ok() -> Result<(), OtToolsIoError> {
let path = get_blank_proj_dirpath().join("project.work");
read_str_file(&path)?;
Ok(())
}
#[test]
fn err_file_io() -> Result<(), OtToolsIoError> {
let path = get_blank_proj_dirpath().join("NOTNTONTONTNTOTNONT");
#[cfg(target_os = "windows")]
assert_eq!(
read_str_file(&path).unwrap_err().to_string(),
format!["File OS Error: The system cannot find the file specified. (os error 2) Path={} (std::io::Error)", path.display()].to_string(),
"should throw a OtToolsIoError::FileOS error when file does not exist"
);
#[cfg(target_os = "linux")]
assert_eq!(
read_str_file(&path).unwrap_err().to_string(),
format![
"File OS Error: No such file or directory (os error 2) Path={} (std::io::Error)",
path.display()
]
.to_string(),
"should throw a OtToolsIoError::FileOS error when file does not exist"
);
Ok(())
}
}
#[doc(hidden)]
fn write_str_file(string: &str, path: &Path) -> Result<(), OtToolsIoError> {
let mut file: File = File::create(path).map_err(|e| OtToolsIoError::FileOs {
path: path.to_path_buf(),
source: e,
})?;
write!(file, "{string}")?;
Ok(())
}
#[cfg(test)]
mod write_str_file {
use crate::{write_str_file, OtToolsIoError};
use std::env::temp_dir;
use std::fs::{create_dir_all, remove_file};
#[test]
fn ok() -> Result<(), OtToolsIoError> {
let path = temp_dir()
.join("ot-tools-io")
.join("write_str_file")
.join("ok.txt");
create_dir_all(path.parent().unwrap())?;
if path.exists() {
remove_file(&path)?;
};
write_str_file("SOMETHING", &path)?;
Ok(())
}
#[test]
fn err_file_io() -> Result<(), OtToolsIoError> {
let path = temp_dir().join("ot-tools-io").join("write_str_file");
create_dir_all(&path)?;
#[cfg(target_os = "windows")]
assert_eq!(
write_str_file("SOMETHING", &path).unwrap_err().to_string(),
format![
"File OS Error: Access is denied. (os error 5) Path={} (std::io::Error)",
path.display()
]
.to_string(),
"should throw a OtToolsIoError::FileOS error when a file cannot be created"
);
#[cfg(target_os = "linux")]
assert_eq!(
write_str_file("SOMETHING", &path).unwrap_err().to_string(),
format![
"File OS Error: Is a directory (os error 21) Path={} (std::io::Error)",
path.display()
]
.to_string(),
"should throw a OtToolsIoError::FileOS error when a file cannot be created"
);
Ok(())
}
}
fn loop_point_is_in_trim_range(loop_point: u32, trim_start: u32, trim_end: u32) -> bool {
loop_point >= trim_start && loop_point < trim_end
}