Skip to content

Commit

Permalink
Add proper import-export options.
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrej33 committed Apr 21, 2024
1 parent 5c07b5b commit a5c5f12
Show file tree
Hide file tree
Showing 34 changed files with 706 additions and 160 deletions.
2 changes: 1 addition & 1 deletion src-tauri/src/app/state/editor/_state_editor_session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::app::state::editor::TabBarState;
use crate::app::state::{Consumed, Session, SessionHelper, SessionState};
use crate::app::{AeonError, DynError};
use crate::debug;
use crate::sketchbook::sketch::Sketch;
use crate::sketchbook::Sketch;

/// The state of one editor session.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,10 @@ use crate::app::event::Event;
use crate::app::state::{Consumed, SessionHelper, SessionState};
use crate::app::DynError;
use crate::sketchbook::data_structs::SketchData;
use crate::sketchbook::model::ModelState;
use crate::sketchbook::observations::ObservationManager;
use crate::sketchbook::properties::PropertyManager;
use crate::sketchbook::{JsonSerde, Manager};
use serde::{Deserialize, Serialize};
use crate::sketchbook::event_utils::make_state_change;
use crate::sketchbook::{JsonSerde, Sketch};
use std::fs::File;
use std::io::Write;

/// Object encompassing all of the individual modules of the Boolean network sketch.
///
/// Most of the actual functionality is implemented by the modules themselves, `Sketch`
/// currently only distributes events and handles situations when cooperation between
/// modules is needed.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Sketch {
model: ModelState,
observations: ObservationManager,
properties: PropertyManager,
}

impl<'de> JsonSerde<'de> for Sketch {}
impl Manager for Sketch {}

impl Default for Sketch {
/// Default empty sketch.
fn default() -> Sketch {
Sketch {
model: ModelState::default(),
observations: ObservationManager::default(),
properties: PropertyManager::default(),
}
}
}
use std::io::{Read, Write};

impl SessionHelper for Sketch {}

Expand All @@ -47,6 +18,15 @@ impl SessionState for Sketch {
self.observations.perform_event(event, at_path)
} else if let Some(at_path) = Self::starts_with("properties", at_path) {
self.properties.perform_event(event, at_path)
} else if Self::starts_with("new_sketch", at_path).is_some() {
self.set_to_empty();
let sketch_data = SketchData::new(&self.model, &self.observations, &self.properties);
let state_change = make_state_change(&["sketch", "set_all"], &sketch_data);
// this is probably one of the real irreversible changes
Ok(Consumed::Irreversible {
state_change,
reset: true,
})
} else if Self::starts_with("export_sketch", at_path).is_some() {
let sketch_data = SketchData::new(&self.model, &self.observations, &self.properties);
let path = Self::clone_payload_str(event, "sketch")?;
Expand All @@ -55,6 +35,23 @@ impl SessionState for Sketch {
file.write_all(sketch_data.to_pretty_json_str().as_bytes())
.map_err(|e| e.to_string())?;
Ok(Consumed::NoChange)
} else if Self::starts_with("import_sketch", at_path).is_some() {
let file_path = Self::clone_payload_str(event, "sketch")?;
// read the file contents
let mut file = File::open(file_path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;

// parse the SketchData, modify the sketch
let sketch_data = SketchData::from_json_str(&contents)?;
self.modify_from_sketch_data(&sketch_data)?;

let state_change = make_state_change(&["sketch", "set_all"], &sketch_data);
// this is probably one of the real irreversible changes
Ok(Consumed::Irreversible {
state_change,
reset: true,
})
} else {
Self::invalid_path_error_generic(at_path)
}
Expand Down
80 changes: 80 additions & 0 deletions src-tauri/src/sketchbook/_sketch/_impl_sketch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::sketchbook::data_structs::SketchData;
use crate::sketchbook::model::ModelState;
use crate::sketchbook::observations::{Dataset, ObservationManager};
use crate::sketchbook::properties::{DynProperty, PropertyManager, StatProperty};
use crate::sketchbook::Sketch;

impl Sketch {
/// Parse and validate all components of `Sketch` from a corresponding `SketchData` instance.
pub fn components_from_sketch_data(
sketch_data: &SketchData,
) -> Result<(ModelState, ObservationManager, PropertyManager), String> {
let datasets = sketch_data
.datasets
.iter()
.map(|d| d.to_dataset())
.collect::<Result<Vec<Dataset>, String>>()?;
let dyn_properties = sketch_data
.dyn_properties
.iter()
.map(|prop_data| prop_data.to_property())
.collect::<Result<Vec<DynProperty>, String>>()?;
let stat_properties = sketch_data
.stat_properties
.iter()
.map(|prop_data| prop_data.to_property())
.collect::<Result<Vec<StatProperty>, String>>()?;

let model = ModelState::new_from_model_data(&sketch_data.model)?;
let obs_manager = ObservationManager::from_datasets(
sketch_data
.datasets
.iter()
.map(|d| d.id.as_str())
.zip(datasets)
.collect(),
)?;
let prop_manager = PropertyManager::new_from_properties(
sketch_data
.dyn_properties
.iter()
.map(|d| d.id.as_str())
.zip(dyn_properties)
.collect(),
sketch_data
.dyn_properties
.iter()
.map(|d| d.id.as_str())
.zip(stat_properties)
.collect(),
)?;
Ok((model, obs_manager, prop_manager))
}

/// Create a new `Sketch` instance given a corresponding `SketchData` object.
pub fn new_from_sketch_data(sketch_data: &SketchData) -> Result<Sketch, String> {
let (model, obs_manager, prop_manager) = Self::components_from_sketch_data(sketch_data)?;
Ok(Sketch {
model,
observations: obs_manager,
properties: prop_manager,
})
}

/// Modify this `Sketch` instance by loading all its components from a corresponding
/// `SketchData` instance. The original sketch information is forgotten.
pub fn modify_from_sketch_data(&mut self, sketch_data: &SketchData) -> Result<(), String> {
let (model, obs_manager, prop_manager) = Self::components_from_sketch_data(sketch_data)?;
self.model = model;
self.observations = obs_manager;
self.properties = prop_manager;
Ok(())
}

/// Modify this `Sketch` instance to a default (empty) settings.
pub fn set_to_empty(&mut self) {
self.model = ModelState::default();
self.observations = ObservationManager::default();
self.properties = PropertyManager::default();
}
}
36 changes: 36 additions & 0 deletions src-tauri/src/sketchbook/_sketch/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::sketchbook::model::ModelState;
use crate::sketchbook::observations::ObservationManager;
use crate::sketchbook::properties::PropertyManager;
use crate::sketchbook::{JsonSerde, Manager};
use serde::{Deserialize, Serialize};

/// **(internal)** Implementation of event-based API for the [SessionState] trait.
mod _impl_session_state;
/// **(internal)** Utility methods for `Sketch`.
mod _impl_sketch;

/// Object encompassing all of the individual modules of the Boolean network sketch.
///
/// Most of the actual functionality is implemented by the modules themselves, `Sketch`
/// currently only distributes events and handles situations when cooperation between
/// modules is needed.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Sketch {
model: ModelState,
observations: ObservationManager,
properties: PropertyManager,
}

impl<'de> JsonSerde<'de> for Sketch {}
impl Manager for Sketch {}

impl Default for Sketch {
/// Default empty sketch.
fn default() -> Sketch {
Sketch {
model: ModelState::default(),
observations: ObservationManager::default(),
properties: PropertyManager::default(),
}
}
}
14 changes: 7 additions & 7 deletions src-tauri/src/sketchbook/_tests_events/_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ fn test_remove_var_complex() {
model.update_position(layout_id, &var_a, 1., 1.).unwrap();

// expected result
let mut model_expected = ModelState::new();
let mut model_expected = ModelState::new_empty();
model_expected.add_var_by_str("b", "b").unwrap();
model_expected.add_regulation_by_str("b -> b").unwrap();

Expand Down Expand Up @@ -136,7 +136,7 @@ fn test_set_update_fn() {
#[test]
/// Test that several kinds of invalid operations fail successfully.
fn test_invalid_var_events() {
let mut model = ModelState::new();
let mut model = ModelState::new_empty();
let var_id = model.generate_var_id("a");
model.add_var(var_id.clone(), "a-name").unwrap();
let model_orig = model.clone();
Expand Down Expand Up @@ -232,7 +232,7 @@ fn test_remove_reg() {
#[test]
/// Test changing position of a layout node via event.
fn test_change_position() {
let mut model = ModelState::new();
let mut model = ModelState::new_empty();
let layout_id = ModelState::get_default_layout_id();
let var_id = model.generate_var_id("a");
model.add_var(var_id.clone(), "a_name").unwrap();
Expand All @@ -253,9 +253,9 @@ fn test_change_position() {
#[test]
/// Test changing monotonicity and essentiality of uninterpreted function's argument via event.
fn test_change_fn_arg_monotonicity_essentiality() {
let mut model = ModelState::new();
let mut model = ModelState::new_empty();
let f = model.generate_uninterpreted_fn_id("f");
model.add_new_uninterpreted_fn(f.clone(), "f", 2).unwrap();
model.add_empty_uninterpreted_fn(f.clone(), "f", 2).unwrap();
let model_orig = model.clone();

// test event for changing uninterpreted fn's monotonicity
Expand Down Expand Up @@ -284,9 +284,9 @@ fn test_change_fn_arg_monotonicity_essentiality() {
#[test]
/// Test changing uninterpreted function's expression via event.
fn test_change_fn_expression() {
let mut model = ModelState::new();
let mut model = ModelState::new_empty();
let f = model.generate_uninterpreted_fn_id("f");
model.add_new_uninterpreted_fn(f.clone(), "f", 2).unwrap();
model.add_empty_uninterpreted_fn(f.clone(), "f", 2).unwrap();
let model_orig = model.clone();

// test event for changing uninterpreted fn's expression
Expand Down
16 changes: 13 additions & 3 deletions src-tauri/src/sketchbook/data_structs/_layout_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl<'de> JsonSerde<'de> for LayoutData {}
impl<'de> JsonSerde<'de> for LayoutMetaData {}

impl LayoutData {
/// Create new `LayoutData` object given a `layout` and its id.
/// Create new `LayoutData` instance given a `layout` and its id.
pub fn from_layout(layout_id: &LayoutId, layout: &Layout) -> LayoutData {
let nodes = layout
.layout_nodes()
Expand All @@ -45,18 +45,28 @@ impl LayoutData {
nodes,
}
}

/// Extract new `Layout` instance from this data.
pub fn to_layout(&self) -> Result<Layout, String> {
let var_node_pairs = self
.nodes
.iter()
.map(|node_data| (node_data.variable.as_str(), node_data.to_node()))
.collect();
Layout::new(&self.name, var_node_pairs)
}
}

impl LayoutMetaData {
/// Create new `LayoutMetaData` object given a layout's name and id string slices.
/// Create new `LayoutMetaData` instance given a layout's name and id string slices.
pub fn new(layout_id: &str, layout_name: &str) -> LayoutMetaData {
LayoutMetaData {
id: layout_id.to_string(),
name: layout_name.to_string(),
}
}

/// Create new `LayoutMetaData` object given a `layout` and its id.
/// Create new `LayoutMetaData` instance given a `layout` and its id.
pub fn from_layout(layout_id: &LayoutId, layout: &Layout) -> LayoutMetaData {
LayoutMetaData::new(layout_id.as_str(), layout.get_layout_name())
}
Expand Down
8 changes: 8 additions & 0 deletions src-tauri/src/sketchbook/data_structs/_layout_node_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct LayoutNodeData {
impl<'de> JsonSerde<'de> for LayoutNodeData {}

impl LayoutNodeData {
/// Create new `LayoutNodeData` instance given a node's layout ID, variable ID, and coordinates.
pub fn new(layout_id: &str, var_id: &str, px: f32, py: f32) -> LayoutNodeData {
LayoutNodeData {
layout: layout_id.to_string(),
Expand All @@ -31,6 +32,8 @@ impl LayoutNodeData {
}
}

/// Create new `LayoutNodeData` instance given a node's layout ID, variable ID,
/// and corresponding `LayoutNode`.
pub fn from_node(layout_id: &LayoutId, var_id: &VarId, node: &LayoutNode) -> LayoutNodeData {
LayoutNodeData::new(
layout_id.as_str(),
Expand All @@ -39,4 +42,9 @@ impl LayoutNodeData {
node.get_py(),
)
}

/// Extract new `LayoutNode` instance from this data.
pub fn to_node(&self) -> LayoutNode {
LayoutNode::new(self.px, self.py)
}
}
2 changes: 1 addition & 1 deletion src-tauri/src/sketchbook/data_structs/_model_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl<'de> JsonSerde<'de> for ModelData {}

impl ModelData {
/// Create new `SketchData` instance given a reference to a model manager instance.
pub fn new(model: &ModelState) -> ModelData {
pub fn from_model(model: &ModelState) -> ModelData {
let mut variables: Vec<_> = model
.variables()
.map(|(id, v)| VariableData::from_var(id, v, model.get_update_fn(id).unwrap()))
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/src/sketchbook/data_structs/_observation_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub struct ObservationData {
impl<'de> JsonSerde<'de> for ObservationData {}

impl ObservationData {
/// Create new `ObservationData` object given `id` and values string slices.
/// Create new `ObservationData` instance given `id` and values string slices.
pub fn new(obs_id: &str, dataset_id: &str, values: &str) -> ObservationData {
ObservationData {
id: obs_id.to_string(),
Expand All @@ -27,7 +27,7 @@ impl ObservationData {
}
}

/// Create new `ObservationData` object given a reference to a observation, and ID of
/// Create new `ObservationData` instance given a reference to a observation, and ID of
/// its dataset.
pub fn from_obs(obs: &Observation, dataset_id: &DatasetId) -> ObservationData {
ObservationData::new(
Expand Down
Loading

0 comments on commit a5c5f12

Please sign in to comment.