Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions raumklang-gui/src/data.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod chart;
pub mod directory;
pub mod frequency_response;
pub mod impulse_response;
pub mod measurement;
Expand Down
12 changes: 12 additions & 0 deletions raumklang-gui/src/data/directory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use std::path::Path;
use std::sync::LazyLock;

pub fn data() -> &'static Path {
PROJECT
.as_ref()
.map(directories::ProjectDirs::data_dir)
.unwrap_or(Path::new("./data"))
}

static PROJECT: LazyLock<Option<directories::ProjectDirs>> =
LazyLock::new(|| directories::ProjectDirs::from("de", "henku", "raumklang"));
114 changes: 109 additions & 5 deletions raumklang-gui/src/data/project.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,74 @@
use serde::{Deserialize, Serialize};
use tokio::fs;

use std::{
io,
fmt, io,
path::{Path, PathBuf},
};

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Project {
pub loopback: Option<Loopback>,
pub measurements: Vec<Measurement>,
#[serde(default)]
pub measurement_operation: Operation,
pub export_from_memory: bool,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Loopback(pub Measurement);

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
impl Loopback {
pub fn new(path: PathBuf) -> Self {
Self(Measurement { path })
}

pub async fn copy(&mut self, dest: impl AsRef<Path>) {
self.0.copy(dest).await
}

pub async fn rename(&mut self, dest: impl AsRef<Path>) {
self.0.rename(dest).await
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Measurement {
pub path: PathBuf,
}

impl Measurement {
pub fn new(path: PathBuf) -> Self {
Self { path }
}

pub async fn copy(&mut self, dest: impl AsRef<Path>) {
let dest = dest.as_ref().with_file_name(self.path.file_name().unwrap());

if self.path == dest {
return;
}

dbg!(&self.path, &dest);
fs::copy(&self.path, &dest).await.unwrap();

self.path = dest;
}

// WARNING: will only work when both files are on the same mount point
pub async fn rename(&mut self, dest: impl AsRef<Path>) {
let dest = dest.as_ref().with_file_name(self.path.file_name().unwrap());

if self.path == dest {
return;
}

fs::rename(&self.path, &dest).await.unwrap();

self.path = dest;
}
}

#[derive(thiserror::Error, Debug, Clone)]
pub enum Error {
#[error("could not load file: {0}")]
Expand All @@ -38,14 +90,66 @@ impl Project {
Ok(project)
}

pub async fn save(self, path: impl AsRef<Path>) -> Result<(), Error> {
pub async fn save(mut self, path: impl AsRef<Path>) -> Result<Self, Error> {
let path = path.as_ref();

if let Some(parent) = path.parent() {
fs::create_dir_all(&parent).await.unwrap();
}

match self.measurement_operation {
Operation::None => {}
Operation::Copy => {
if let Some(loopback) = self.loopback.as_mut() {
loopback.copy(&path).await;
}

for m in self.measurements.iter_mut() {
m.copy(&path).await;
}
}
Operation::Move => {
if let Some(loopback) = self.loopback.as_mut() {
loopback.rename(&path).await;
}

for m in self.measurements.iter_mut() {
m.rename(&path).await;
}
}
}

let json =
serde_json::to_string_pretty(&self).map_err(|err| Error::Json(err.to_string()))?;

tokio::fs::write(path, json)
.await
.map_err(|err| Error::Io(err.kind()))?;

Ok(())
Ok(self)
}
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum Operation {
#[default]
None,
Copy,
Move,
}

impl Operation {
pub const ALL: &[Operation] = &[Operation::None, Operation::Copy, Operation::Move];
}

impl fmt::Display for Operation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Operation::None => "do nothing",
Operation::Copy => "copy to project directory",
Operation::Move => "move to project directory",
};

write!(f, "{}", s)
}
}
9 changes: 5 additions & 4 deletions raumklang-gui/src/data/recent_projects.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::data::directory;

use super::Error;

use std::{
Expand Down Expand Up @@ -97,12 +99,11 @@ impl<'a> IntoIterator for &'a RecentProjects {
}

async fn data_dir() -> Result<PathBuf, io::Error> {
let app_dir = directories::ProjectDirs::from("de", "HenKu", "raumklang").unwrap();
let data_dir = app_dir.data_local_dir().to_path_buf();
let path = directory::data();

tokio::fs::create_dir_all(&data_dir).await?;
tokio::fs::create_dir_all(&path).await?;

Ok(data_dir)
Ok(path.to_path_buf())
}

#[cfg(test)]
Expand Down
6 changes: 3 additions & 3 deletions raumklang-gui/src/data/spectrogram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustfft::{
use crate::data::{SampleRate, Samples};

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct Preferences {
pub struct Config {
pub span_before_peak: Duration,
pub span_after_peak: Duration,
pub window_width: Duration,
Expand All @@ -34,7 +34,7 @@ impl Spectrogram {

pub(crate) async fn compute(
ir: raumklang_core::ImpulseResponse,
preferences: Preferences,
preferences: Config,
) -> Spectrogram {
let sample_rate = SampleRate::from(ir.sample_rate);

Expand Down Expand Up @@ -114,7 +114,7 @@ impl fmt::Debug for Spectrogram {
}
}

impl Default for Preferences {
impl Default for Config {
fn default() -> Self {
Self {
span_before_peak: Duration::from_millis(200),
Expand Down
Loading