diff --git a/main/src/application/source_model.rs b/main/src/application/source_model.rs index 6dac9bddc..1762762b6 100644 --- a/main/src/application/source_model.rs +++ b/main/src/application/source_model.rs @@ -1,4 +1,3 @@ -use crate::application::SourceProp::ButtonBackgroundImagePath; use crate::application::{ Affected, Change, GetProcessingRelevance, MappingProp, ProcessingRelevance, }; diff --git a/main/src/domain/stream_deck_device.rs b/main/src/domain/stream_deck_device.rs index c4490b115..33b751628 100644 --- a/main/src/domain/stream_deck_device.rs +++ b/main/src/domain/stream_deck_device.rs @@ -1,6 +1,5 @@ use crate::domain::UnitId; use base::hash_util::{NonCryptoHashMap, NonCryptoHashSet}; -use derive_more::Display; use hidapi::HidApi; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; diff --git a/main/src/infrastructure/data/instance_data.rs b/main/src/infrastructure/data/instance_data.rs index dba50933b..d59452f84 100644 --- a/main/src/infrastructure/data/instance_data.rs +++ b/main/src/infrastructure/data/instance_data.rs @@ -1,17 +1,10 @@ use crate::infrastructure::data::{ClipMatrixRefData, UnitData}; +use anyhow::{anyhow, bail, Context}; use base::default_util::{deserialize_null_default, is_default}; use base::hash_util::NonCryptoHashMap; use helgobox_api::persistence::InstanceSettings; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum InstanceOrUnitData { - InstanceData(InstanceData), - /// For backward compatibility. - UnitData(UnitData), -} - #[derive(Clone, Debug, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct InstanceData { @@ -43,27 +36,50 @@ pub struct InstanceData { pub custom_data: NonCryptoHashMap, } -impl Default for InstanceOrUnitData { - fn default() -> Self { - Self::InstanceData(InstanceData::default()) +impl InstanceData { + pub fn parse(data: &[u8]) -> anyhow::Result { + let instance_data: Self = match serde_json::from_slice(data) { + Ok(d) => d, + Err(instance_data_error) => { + // Couldn't parse as instance data. Unhappy path. Let's make some inspections. + let json: serde_json::Value = serde_json::from_slice(data) + .with_context(|| create_parse_error_msg(data, "parsing as JSON"))?; + // Parsing was successful + if json.get("mainUnit").is_some() { + // It's really meant as InstanceData. Fail! + Err(instance_data_error).with_context(|| { + create_parse_error_msg(data, "interpreting JSON as instance data") + })?; + } + // Ah, this could be a preset for the pre-2.16 era, meant as data for a single unit. + let data: UnitData = serde_json::from_value(json).with_context(|| { + create_parse_error_msg(data, "interpreting JSON as unit data") + })?; + convert_old_unit_to_instance_data(data) + } + }; + Ok(instance_data) } } -impl InstanceOrUnitData { - #[allow(deprecated)] - pub fn into_instance_data(self) -> InstanceData { - match self { - InstanceOrUnitData::InstanceData(d) => d, - InstanceOrUnitData::UnitData(mut d) => InstanceData { - // Migrate pot state from unit data - pot_state: d.pot_state.take().unwrap_or_default(), - // Migrate clip matrix state from unit data - clip_matrix: d.clip_matrix.take(), - main_unit: d, - additional_units: vec![], - settings: Default::default(), - custom_data: Default::default(), - }, - } +fn create_parse_error_msg(data: &[u8], label: &str) -> String { + let data_as_str = std::str::from_utf8(data).unwrap_or(""); + format!( + "Helgobox couldn't restore this instance while {label}. Please attach the following text if you want to report this: \n\n\ + {data_as_str}\n\n" + ) +} + +#[allow(deprecated)] +fn convert_old_unit_to_instance_data(mut d: UnitData) -> InstanceData { + InstanceData { + // Migrate pot state from unit data + pot_state: d.pot_state.take().unwrap_or_default(), + // Migrate clip matrix state from unit data + clip_matrix: d.clip_matrix.take(), + main_unit: d, + additional_units: vec![], + settings: Default::default(), + custom_data: Default::default(), } } diff --git a/main/src/infrastructure/plugin/helgobox_plugin.rs b/main/src/infrastructure/plugin/helgobox_plugin.rs index e9e0a5346..721c4bb03 100644 --- a/main/src/infrastructure/plugin/helgobox_plugin.rs +++ b/main/src/infrastructure/plugin/helgobox_plugin.rs @@ -25,7 +25,7 @@ use std::sync::{Arc, OnceLock}; use crate::infrastructure::plugin::backbone_shell::BackboneShell; -use crate::infrastructure::data::InstanceOrUnitData; +use crate::infrastructure::data::InstanceData; use crate::infrastructure::plugin::helgobox_plugin_editor::HelgoboxPluginEditor; use crate::infrastructure::plugin::instance_shell::InstanceShell; use crate::infrastructure::ui::instance_panel::InstancePanel; @@ -414,9 +414,7 @@ impl HelgoboxPlugin { match param_name { SET_STATE_PARAM_NAME => { let c_str = unsafe { CStr::from_ptr(buffer) }; - let rust_str = c_str.to_str().expect("not valid UTF-8"); - let data: InstanceOrUnitData = serde_json::from_str(rust_str) - .context("couldn't deserialize instance or unit data")?; + let data = InstanceData::parse(c_str.to_bytes())?; let lazy_data = self.lazy_data.get().context("lazy data not yet set")?; lazy_data.instance_shell.clone().apply_data(data)?; Ok(()) diff --git a/main/src/infrastructure/plugin/instance_parameter_container.rs b/main/src/infrastructure/plugin/instance_parameter_container.rs index 144dee922..e5501b4c1 100644 --- a/main/src/infrastructure/plugin/instance_parameter_container.rs +++ b/main/src/infrastructure/plugin/instance_parameter_container.rs @@ -3,7 +3,7 @@ use reaper_low::firewall; use std::sync; use crate::domain::{ParameterManager, PluginParamIndex}; -use crate::infrastructure::data::InstanceOrUnitData; +use crate::infrastructure::data::InstanceData; use crate::infrastructure::plugin::instance_shell::InstanceShell; use reaper_high::Reaper; use std::sync::{Arc, OnceLock, RwLock}; @@ -175,7 +175,7 @@ impl PluginParameters for InstanceParameterContainer { .instance_shell .upgrade() .expect("instance shell gone") - .apply_data(InstanceOrUnitData::default()) + .apply_data(InstanceData::default()) .expect("couldn't load factory default"); } return; diff --git a/main/src/infrastructure/plugin/instance_shell.rs b/main/src/infrastructure/plugin/instance_shell.rs index 98b05ab27..734beb3b5 100644 --- a/main/src/infrastructure/plugin/instance_shell.rs +++ b/main/src/infrastructure/plugin/instance_shell.rs @@ -3,7 +3,7 @@ use crate::domain::{ ControlEvent, IncomingMidiMessage, Instance, InstanceHandler, InstanceId, MidiEvent, ProcessorContext, SharedInstance, SharedRealTimeInstance, UnitId, }; -use crate::infrastructure::data::{InstanceData, InstanceOrUnitData, UnitData}; +use crate::infrastructure::data::{InstanceData, UnitData}; use crate::infrastructure::plugin::unit_shell::UnitShell; use crate::infrastructure::plugin::{update_auto_units_async, BackboneShell}; use crate::infrastructure::ui::instance_panel::InstancePanel; @@ -402,8 +402,7 @@ impl InstanceShell { /// Must be called from the main thread. pub fn save(&self) -> Vec { let instance_data = self.create_data(); - let data = InstanceOrUnitData::InstanceData(instance_data); - serde_json::to_vec(&data).expect("couldn't serialize instance data") + serde_json::to_vec(&instance_data).expect("couldn't serialize instance data") } fn remove_auto_unit_if_requirements_met( @@ -487,32 +486,22 @@ impl InstanceShell { // ReaLearn C++ saved some IPlug binary data in front of the actual JSON object. Find // start of JSON data. let data = &data[left_json_object_brace..]; - let data: InstanceOrUnitData = match serde_json::from_slice(data) { - Ok(d) => d, - Err(e) => { - bail!( - "Helgobox couldn't restore this unit: {}\n\nPlease also attach the following text when reporting this: \n\n{}", - e, - std::str::from_utf8(data).unwrap_or("UTF-8 decoding error") - ) - } - }; - self.apply_data_internal(data)?; + let instance_data = InstanceData::parse(data)?; + self.apply_data_internal(instance_data)?; Ok(()) } pub fn apply_data( self: SharedInstanceShell, - instance_data: InstanceOrUnitData, + instance_data: InstanceData, ) -> anyhow::Result<()> { self.apply_data_internal(instance_data) } fn apply_data_internal( self: SharedInstanceShell, - instance_data: InstanceOrUnitData, + instance_data: InstanceData, ) -> anyhow::Result<()> { - let instance_data = instance_data.into_instance_data(); let instance = self.instance(); // General properties *self.settings.get().borrow_mut() = instance_data.settings; diff --git a/main/src/infrastructure/ui/header_panel.rs b/main/src/infrastructure/ui/header_panel.rs index 3427ce1c0..c2ed6884d 100644 --- a/main/src/infrastructure/ui/header_panel.rs +++ b/main/src/infrastructure/ui/header_panel.rs @@ -24,8 +24,8 @@ use crate::domain::{ use crate::domain::{MidiControlInput, MidiDestination}; use crate::infrastructure::data::{ CommonCompartmentPresetManager, CommonPresetInfo, CompartmentModelData, - FileBasedMainPresetManager, InstanceOrUnitData, MappingModelData, OscDevice, PresetFileType, - PresetOrigin, UnitData, + FileBasedMainPresetManager, MappingModelData, OscDevice, PresetFileType, PresetOrigin, + UnitData, }; use crate::infrastructure::plugin::{ update_auto_units_async, warn_about_failed_server_start, BackboneShell, @@ -2047,7 +2047,7 @@ impl HeaderPanel { let instance_panel = self.instance_panel(); instance_panel.show_unit(None); let instance_shell = instance_panel.shell()?; - instance_shell.apply_data(InstanceOrUnitData::InstanceData(*d))?; + instance_shell.apply_data(*d)?; } } Tagged(DataObject::Unit(Envelope { value: d, .. })) => {