diff --git a/Cargo.lock b/Cargo.lock index fce9bd8..675e50d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,6 +204,19 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + [[package]] name = "gloo" version = "0.8.0" @@ -520,9 +533,11 @@ dependencies = [ name = "plate-tool" version = "0.1.0" dependencies = [ + "getrandom", "lazy_static", "log", "regex", + "uuid", "wasm-bindgen", "wasm-logger", "web-sys", @@ -530,6 +545,12 @@ dependencies = [ "yewdux", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "prettyplease" version = "0.1.25" @@ -599,6 +620,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "regex" version = "1.8.1" @@ -799,12 +850,40 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +[[package]] +name = "uuid" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" +dependencies = [ + "getrandom", + "rand", + "uuid-macro-internal", +] + +[[package]] +name = "uuid-macro-internal" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f67b459f42af2e6e1ee213cb9da4dbd022d3320788c3fb3e1b893093f1e45da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.16", +] + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.86" diff --git a/Cargo.toml b/Cargo.toml index 258a5d3..195bb7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,10 @@ edition = "2021" yew = { version = "0.20.0", features = ["csr"] } yewdux = "0.9" wasm-bindgen = "0.2" -web-sys = "0.3" +web-sys = { version = "0.3", features = ["FormData", "HtmlFormElement"] } log = "0.4" wasm-logger = "0.2" regex = "1" lazy_static = "1.4" +uuid = { version = "1.3", features = ["v4", "fast-rng", "macro-diagnostics"] } +getrandom = { version = "0.2", features = ["js"] } diff --git a/src/components/states.rs b/src/components/states.rs index 3c355bb..1860810 100644 --- a/src/components/states.rs +++ b/src/components/states.rs @@ -1,5 +1,8 @@ use yewdux::prelude::*; use super::transfer_menu::RegionDisplay; +use crate::data::plate_instances::PlateInstance; +use crate::data::transfer::Transfer; +use crate::data::plate::*; #[derive(Debug, Default, Clone, PartialEq, Store)] pub struct NewTransferState { @@ -8,3 +11,50 @@ pub struct NewTransferState { pub interleave_x: u8, pub interleave_y: u8, } + +#[derive(Default, PartialEq, Clone)] +pub struct MainState { + pub source_plates: Vec, + pub destination_plates: Vec, + pub transfers: Vec, +} + +impl Store for MainState { + fn new() -> Self { + Self { + source_plates: Vec::new(), + destination_plates: Vec::new(), + transfers: Vec::new(), + } + } + + fn should_notify(&self, old: &Self) -> bool { + self != old + } +} + +impl MainState { + pub fn purge_transfers(&mut self) { + // Removes any transfers for which the associated plates are gone + self.transfers = self.transfers.iter() + .filter(|tr| { + self.source_plates.iter().any(|spi| { + spi.get_uuid() == tr.source_id + }) && + self.destination_plates.iter().any(|dpi| { + dpi.get_uuid() == tr.dest_id + }) + }) + .map(|&tr| tr) + .collect(); + } + + pub fn add_source_plate(&mut self, plate: PlateInstance) { + assert!(plate.plate.plate_type == PlateType::Source); + self.source_plates.push(plate); + } + pub fn add_dest_plate(&mut self, plate: PlateInstance) { + assert!(plate.plate.plate_type == PlateType::Destination); + self.destination_plates.push(plate); + } +} diff --git a/src/components/tree.rs b/src/components/tree.rs index 6a5b349..95d4b4e 100644 --- a/src/components/tree.rs +++ b/src/components/tree.rs @@ -1,10 +1,100 @@ #![allow(non_snake_case)] +use std::fmt::format; + +use wasm_bindgen::JsCast; +use web_sys::{EventTarget, HtmlFormElement, FormData}; use yew::prelude::*; +use yewdux::prelude::*; + +use crate::data::{plate_instances::PlateInstance, transfer::Transfer}; +use crate::data::plate::*; +use crate::components::states::MainState; #[function_component] pub fn Tree() -> Html { + let (state, dispatch) = use_store::(); + let source_plates = state.source_plates.iter() + .map(|spi| { + html!{
  • {String::from(spi)}
  • } + }).collect::(); + let dest_plates = state.destination_plates.iter() + .map(|spi| { + html!{
  • {String::from(spi)}
  • } + }).collect::(); + + let new_plate_callback = { + let dispatch = dispatch.clone(); + Callback::from(move |e: SubmitEvent| { + e.prevent_default(); + let target: Option = e.target(); + let form = target.and_then(|t| t.dyn_into::().ok()); + if let Some(form) = form { + if let Ok(form_data) = FormData::new_with_form(&form) { + let name = form_data.get("new_plate_name").as_string().unwrap(); + let format = match form_data.get("plate_format").as_string().unwrap().as_str() { + "384" => PlateFormat::W384, + "96" => PlateFormat::W96, + _ => PlateFormat::W6, + }; + let plate_type = match form_data.get("new_plate_type").as_string().unwrap().as_str() { + "src" => PlateType::Source, + "dest" => PlateType::Destination, + _ => PlateType::Source, + }; + dispatch.reduce_mut(|s| { + if plate_type == PlateType::Source { + s.add_source_plate(PlateInstance::new(PlateType::Source, format, name)) + } else { + s.add_dest_plate(PlateInstance::new(PlateType::Destination, format, name)) + } + }); + } + } + }) + }; + html! {
    +
    +

    {"Source Plates:"}

    +
      + {source_plates} +
    +
    +
    +

    {"Destination Plates:"}

    +
      + {dest_plates} +
    +
    +
    +

    {"Transfers:"}

    +
      +
    +
    + // Temporary +
    +
    +
    + + + + + + + +
    +
    } } + +impl From<&PlateInstance> for String { + fn from(value: &PlateInstance) -> Self { + // Could have other formatting here + format!("{}, {}", value.name, value.plate.plate_format) + } +} diff --git a/src/data/mod.rs b/src/data/mod.rs index e065044..c07b5f5 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1,2 +1,4 @@ pub mod plate; pub mod transfer_region; +pub mod transfer; +pub mod plate_instances; diff --git a/src/data/plate.rs b/src/data/plate.rs index 96ab5ed..1e5fdee 100644 --- a/src/data/plate.rs +++ b/src/data/plate.rs @@ -1,3 +1,4 @@ +#[derive(PartialEq, Eq, Clone, Copy)] pub struct Plate { pub plate_type: PlateType, pub plate_format: PlateFormat, @@ -16,11 +17,13 @@ impl Plate { } } +#[derive(PartialEq, Eq, Clone, Copy)] pub enum PlateType { Source, Destination, } +#[derive(PartialEq, Eq, Clone, Copy)] pub enum PlateFormat { W6, W12, @@ -31,6 +34,20 @@ pub enum PlateFormat { W1536, W3456, } +impl std::fmt::Display for PlateFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PlateFormat::W6 => write!(f, "6"), + PlateFormat::W12 => write!(f, "12"), + PlateFormat::W24 => write!(f, "24"), + PlateFormat::W48 => write!(f, "48"), + PlateFormat::W96 => write!(f, "96"), + PlateFormat::W384 => write!(f, "384"), + PlateFormat::W1536 => write!(f, "1536"), + PlateFormat::W3456 => write!(f, "3456"), + } + } +} impl PlateFormat { pub fn size(&self) -> (u8, u8) { diff --git a/src/data/plate_instances.rs b/src/data/plate_instances.rs new file mode 100644 index 0000000..f525fd3 --- /dev/null +++ b/src/data/plate_instances.rs @@ -0,0 +1,33 @@ +use uuid::Uuid; +use super::plate::*; + +#[derive(PartialEq, Clone)] +pub struct PlateInstance { + pub plate: Plate, + id: Uuid, + pub name: String, +} + +impl PlateInstance { + pub fn new(sort: PlateType, format: PlateFormat, name: String) -> Self { + PlateInstance { + plate: Plate { plate_type: sort, plate_format: format }, + id: Uuid::new_v4(), + name, + } + } + + pub fn get_uuid(&self) -> Uuid { + self.id + } + + pub fn change_name(&mut self, new_name: String) { + self.name = new_name; + } +} + +impl From for PlateInstance { + fn from(value: Plate) -> Self { + PlateInstance { plate: value, id: Uuid::new_v4(), name: "New Plate".to_string() } + } +} diff --git a/src/data/transfer.rs b/src/data/transfer.rs new file mode 100644 index 0000000..dbedcdf --- /dev/null +++ b/src/data/transfer.rs @@ -0,0 +1,20 @@ +use uuid::Uuid; +use super::transfer_region::*; +use super::plate_instances::*; + +#[derive(PartialEq, Clone, Copy)] +pub struct Transfer { + pub source_id: Uuid, + pub dest_id: Uuid, + pub transfer: TransferRegion, +} + +impl Transfer { + fn new(source: PlateInstance, dest: PlateInstance, tr: TransferRegion) -> Self { + Self { + source_id: source.get_uuid(), + dest_id: dest.get_uuid(), + transfer: tr + } + } +} diff --git a/src/data/transfer_region.rs b/src/data/transfer_region.rs index fd3d88c..3f94a84 100644 --- a/src/data/transfer_region.rs +++ b/src/data/transfer_region.rs @@ -1,6 +1,6 @@ use super::plate::Plate; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum Region { Rect((u8, u8), (u8, u8)), Point((u8, u8)), @@ -17,16 +17,17 @@ impl TryFrom for ((u8, u8), (u8, u8)) { } } -pub struct TransferRegion<'a> { - pub source_plate: &'a Plate, +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct TransferRegion { + pub source_plate: Plate, pub source_region: Region, // Even if it is just a point, we don't want corners. - pub dest_plate: &'a Plate, + pub dest_plate: Plate, pub dest_region: Region, pub interleave_source: Option<(i8, i8)>, pub interleave_dest: Option<(i8, i8)>, } -impl TransferRegion<'_> { +impl TransferRegion { pub fn get_source_wells(&self) -> Vec<(u8, u8)> { if let Region::Rect(c1, c2) = self.source_region { let mut wells = Vec::<(u8, u8)>::new(); @@ -292,7 +293,7 @@ use std::fmt; use std::ops::Mul; #[cfg(debug_assertions)] // There should be no reason to print a transfer otherwise -impl fmt::Display for TransferRegion<'_> { +impl fmt::Display for TransferRegion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "Source Plate:")?; let source_dims = self.source_plate.size(); @@ -339,9 +340,9 @@ mod tests { let destination = Plate::new(PlateType::Destination, PlateFormat::W384); let transfer1 = TransferRegion { - source_plate: &source, + source_plate: source, source_region: Region::Rect((1, 1), (3, 3)), - dest_plate: &destination, + dest_plate: destination, dest_region: Region::Point((3,3)), interleave_source: None, interleave_dest: None, @@ -352,9 +353,9 @@ mod tests { assert_eq!(transfer1_map((2,2)), Some(vec!{(4,4)}), "Failed basic shift transfer 3"); let transfer2 = TransferRegion { - source_plate: &source, + source_plate: source, source_region: Region::Rect((1, 1), (3, 3)), - dest_plate: &destination, + dest_plate: destination, dest_region: Region::Point((3,3)), interleave_source: Some((2,2)), interleave_dest: None, @@ -366,9 +367,9 @@ mod tests { assert_eq!(transfer2_map((3,3)), Some(vec!{(4,4)}), "Failed source interleave, type simple 4"); let transfer3 = TransferRegion { - source_plate: &source, + source_plate: source, source_region: Region::Rect((1, 1), (3, 3)), - dest_plate: &destination, + dest_plate: destination, dest_region: Region::Point((3,3)), interleave_source: None, interleave_dest: Some((2,3)), @@ -386,9 +387,9 @@ mod tests { let destination = Plate::new(PlateType::Destination, PlateFormat::W384); let transfer1 = TransferRegion { - source_plate: &source, + source_plate: source, source_region: Region::Rect((1, 1), (2, 2)), - dest_plate: &destination, + dest_plate: destination, dest_region: Region::Rect((2,2),(11,11)), interleave_source: None, interleave_dest: Some((3,3)), diff --git a/src/lib.rs b/src/lib.rs index 51fc459..8960deb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,9 +21,9 @@ pub fn plate_test() { let destination = plate::Plate::new(plate::PlateType::Destination, plate::PlateFormat::W384); let transfer = transfer_region::TransferRegion { - source_plate: &source, + source_plate: source, source_region: transfer_region::Region::Rect((1, 1), (2, 2)), - dest_plate: &destination, + dest_plate: destination, dest_region: transfer_region::Region::Rect((2,2),(11,11)), interleave_source: None, interleave_dest: Some((3,3)),