use schemars::JsonSchema;
use serde::{Deserialize, Deserializer};
use super::Shape;
use super::form::{Presets, preset_or, presets};
use crate::gpu::shaders as wgsl;
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema)]
#[serde(remote = "Self", rename_all = "camelCase", deny_unknown_fields)]
#[schemars(transform = presets::<Port>)]
pub struct Port {
pub side: Side,
#[serde(default)]
pub order: Option<u32>,
#[serde(default)]
pub label: Option<String>,
#[serde(default)]
pub shape: Shape,
#[serde(default)]
pub output: bool,
}
impl Presets for Port {
const NAMES: &'static [&'static str] = &["left", "right", "top", "bottom"];
fn preset(name: &str) -> Option<Port> {
let side = [Side::Left, Side::Right, Side::Top, Side::Bottom];
Port::NAMES.iter().position(|n| *n == name).map(|i| Port::new(side[i]))
}
fn options<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Port, D::Error> {
Port::deserialize(deserializer)
}
}
impl<'de> Deserialize<'de> for Port {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Port, D::Error> {
preset_or(deserializer)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum Side {
Left,
Right,
Top,
Bottom,
}
impl Port {
pub fn new(side: Side) -> Port {
Port { side, order: None, label: None, shape: Shape::Ellipse, output: false }
}
pub fn output(side: Side) -> Port {
Port { output: true, ..Port::new(side) }
}
#[must_use]
pub fn label(mut self, label: impl Into<String>) -> Port {
self.label = Some(label.into());
self
}
pub(crate) fn codes(ports: &[&Port]) -> Vec<u32> {
(0..ports.len())
.map(|i| {
let port = ports[i];
let mut same: Vec<usize> = (0..ports.len()).filter(|&j| ports[j].side == port.side).collect();
same.sort_by_key(|&j| (ports[j].order.unwrap_or(u32::MAX), j));
let place = same.iter().position(|&j| j == i).unwrap_or(0) as u32;
let output = if port.output { wgsl::PORT_OUTPUT } else { 0 };
port.side.code() | (place.min(255) << 2) | ((same.len() as u32).min(255) << 10) | output
})
.collect()
}
}
impl From<Side> for Port {
fn from(side: Side) -> Port {
Port::new(side)
}
}
impl Side {
pub(crate) fn code(self) -> u32 {
match self {
Side::Left => 0,
Side::Right => 1,
Side::Top => 2,
Side::Bottom => 3,
}
}
pub(crate) fn from_code(code: u32) -> Option<Side> {
[Side::Left, Side::Right, Side::Top, Side::Bottom].get(code as usize).copied()
}
}