Files
Cagire/crates/project/src/file.rs

110 lines
2.7 KiB
Rust

use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::project::{Bank, Project};
const VERSION: u8 = 1;
pub const EXTENSION: &str = "cagire";
pub fn ensure_extension(path: &Path) -> PathBuf {
if path.extension().map(|e| e == EXTENSION).unwrap_or(false) {
path.to_path_buf()
} else {
path.with_extension(EXTENSION)
}
}
#[derive(Serialize, Deserialize)]
struct ProjectFile {
version: u8,
banks: Vec<Bank>,
#[serde(default)]
sample_paths: Vec<PathBuf>,
#[serde(default = "default_tempo")]
tempo: f64,
#[serde(default)]
playing_patterns: Vec<(usize, usize)>,
#[serde(default, skip_serializing_if = "String::is_empty")]
prelude: String,
}
fn default_tempo() -> f64 {
120.0
}
impl From<&Project> for ProjectFile {
fn from(project: &Project) -> Self {
Self {
version: VERSION,
banks: project.banks.clone(),
sample_paths: project.sample_paths.clone(),
tempo: project.tempo,
playing_patterns: project.playing_patterns.clone(),
prelude: project.prelude.clone(),
}
}
}
impl From<ProjectFile> for Project {
fn from(file: ProjectFile) -> Self {
let mut project = Self {
banks: file.banks,
sample_paths: file.sample_paths,
tempo: file.tempo,
playing_patterns: file.playing_patterns,
prelude: file.prelude,
};
project.normalize();
project
}
}
#[derive(Debug)]
pub enum FileError {
Io(io::Error),
Json(serde_json::Error),
Version(u8),
}
impl std::fmt::Display for FileError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FileError::Io(e) => write!(f, "IO error: {e}"),
FileError::Json(e) => write!(f, "JSON error: {e}"),
FileError::Version(v) => write!(f, "Unsupported version: {v}"),
}
}
}
impl From<io::Error> for FileError {
fn from(e: io::Error) -> Self {
FileError::Io(e)
}
}
impl From<serde_json::Error> for FileError {
fn from(e: serde_json::Error) -> Self {
FileError::Json(e)
}
}
pub fn save(project: &Project, path: &Path) -> Result<PathBuf, FileError> {
let path = ensure_extension(path);
let file = ProjectFile::from(project);
let json = serde_json::to_string_pretty(&file)?;
fs::write(&path, json)?;
Ok(path)
}
pub fn load(path: &Path) -> Result<Project, FileError> {
let json = fs::read_to_string(path)?;
let file: ProjectFile = serde_json::from_str(&json)?;
if file.version > VERSION {
return Err(FileError::Version(file.version));
}
Ok(Project::from(file))
}