use std::fmt;
use powerio_core::PioModule;
use powerio_dist::MulticonductorNetwork;
use powerio_tx::BalancedNetwork;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum PioValueKind {
BalancedNetwork,
MulticonductorNetwork,
BalancedNetworkTimeSeries,
BalancedOperatingPointTimeSeries,
MulticonductorOperatingPointTimeSeries,
BalancedNetworkScenarioSet,
DcPfInstance,
AcPfInstance,
DcOpfInstance,
AcOpfInstance,
McAcPfInstance,
McAcOpfInstance,
AcScucInstance,
DcPfSolution,
AcPfSolution,
DcOpfSolution,
AcOpfSolution,
McAcPfSolution,
McAcOpfSolution,
AcScucSolution,
}
impl PioValueKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::BalancedNetwork => "balanced_network",
Self::BalancedNetworkTimeSeries => "balanced_network_time_series",
Self::BalancedOperatingPointTimeSeries => "balanced_operating_point_time_series",
Self::MulticonductorOperatingPointTimeSeries => {
"multiconductor_operating_point_time_series"
}
Self::BalancedNetworkScenarioSet => "balanced_network_scenario_set",
Self::MulticonductorNetwork => "multiconductor_network",
Self::DcPfInstance => "dc_pf_instance",
Self::AcPfInstance => "ac_pf_instance",
Self::DcOpfInstance => "dc_opf_instance",
Self::AcOpfInstance => "ac_opf_instance",
Self::McAcPfInstance => "mc_ac_pf_instance",
Self::McAcOpfInstance => "mc_ac_opf_instance",
Self::AcScucInstance => "ac_scuc_instance",
Self::DcPfSolution => "dc_pf_solution",
Self::AcPfSolution => "ac_pf_solution",
Self::DcOpfSolution => "dc_opf_solution",
Self::AcOpfSolution => "ac_opf_solution",
Self::McAcPfSolution => "mc_ac_pf_solution",
Self::McAcOpfSolution => "mc_ac_opf_solution",
Self::AcScucSolution => "ac_scuc_solution",
}
}
}
impl fmt::Display for PioValueKind {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Debug)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
pub enum PioValue {
BalancedNetwork(BalancedNetwork),
MulticonductorNetwork(MulticonductorNetwork),
BalancedNetworkTimeSeries(powerio_core::TimeSeries<BalancedNetwork>),
BalancedOperatingPointTimeSeries(powerio_prob::BalancedOperatingPoints),
MulticonductorOperatingPointTimeSeries(powerio_prob::MulticonductorOperatingPoints),
BalancedNetworkScenarioSet(powerio_core::ScenarioSet<BalancedNetwork>),
DcPfInstance(powerio_prob::DcPfInstance),
AcPfInstance(powerio_prob::AcPfInstance),
DcOpfInstance(powerio_prob::DcOpfInstance),
AcOpfInstance(powerio_prob::AcOpfInstance),
McAcPfInstance(powerio_prob::McAcPfInstance),
McAcOpfInstance(powerio_prob::McAcOpfInstance),
AcScucInstance(powerio_prob::AcScucInstance),
DcPfSolution(powerio_prob::DcPfSolution),
AcPfSolution(powerio_prob::AcPfSolution),
DcOpfSolution(powerio_prob::DcOpfSolution),
AcOpfSolution(powerio_prob::AcOpfSolution),
McAcPfSolution(powerio_prob::McAcPfSolution),
McAcOpfSolution(powerio_prob::McAcOpfSolution),
AcScucSolution(powerio_prob::AcScucSolution),
}
impl PioValue {
#[must_use]
pub fn kind(&self) -> PioValueKind {
match self {
Self::BalancedNetwork(_) => PioValueKind::BalancedNetwork,
Self::MulticonductorNetwork(_) => PioValueKind::MulticonductorNetwork,
Self::BalancedNetworkTimeSeries(_) => PioValueKind::BalancedNetworkTimeSeries,
Self::BalancedOperatingPointTimeSeries(_) => {
PioValueKind::BalancedOperatingPointTimeSeries
}
Self::MulticonductorOperatingPointTimeSeries(_) => {
PioValueKind::MulticonductorOperatingPointTimeSeries
}
Self::BalancedNetworkScenarioSet(_) => PioValueKind::BalancedNetworkScenarioSet,
Self::DcPfInstance(_) => PioValueKind::DcPfInstance,
Self::AcPfInstance(_) => PioValueKind::AcPfInstance,
Self::DcOpfInstance(_) => PioValueKind::DcOpfInstance,
Self::AcOpfInstance(_) => PioValueKind::AcOpfInstance,
Self::McAcPfInstance(_) => PioValueKind::McAcPfInstance,
Self::McAcOpfInstance(_) => PioValueKind::McAcOpfInstance,
Self::AcScucInstance(_) => PioValueKind::AcScucInstance,
Self::DcPfSolution(_) => PioValueKind::DcPfSolution,
Self::AcPfSolution(_) => PioValueKind::AcPfSolution,
Self::DcOpfSolution(_) => PioValueKind::DcOpfSolution,
Self::AcOpfSolution(_) => PioValueKind::AcOpfSolution,
Self::McAcPfSolution(_) => PioValueKind::McAcPfSolution,
Self::McAcOpfSolution(_) => PioValueKind::McAcOpfSolution,
Self::AcScucSolution(_) => PioValueKind::AcScucSolution,
}
}
}
#[derive(Debug)]
pub struct ValueKindMismatch {
expected: PioValueKind,
module: PioModule<PioValue>,
}
impl ValueKindMismatch {
#[must_use]
pub fn expected(&self) -> PioValueKind {
self.expected
}
#[must_use]
pub fn actual(&self) -> PioValueKind {
self.module.value().kind()
}
#[must_use]
pub fn into_module(self) -> PioModule<PioValue> {
self.module
}
}
impl fmt::Display for ValueKindMismatch {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"expected `{}`, found `{}`",
self.expected.as_str(),
self.actual().as_str()
)
}
}
impl std::error::Error for ValueKindMismatch {}
mod private {
pub trait Sealed {}
}
#[doc(hidden)]
pub trait FromPioValue: private::Sealed + Sized {
const KIND: PioValueKind;
#[allow(clippy::result_large_err)]
fn try_from_pio_value(value: PioValue) -> Result<Self, PioValue>;
}
#[allow(clippy::result_large_err)]
pub fn try_into_typed<T: FromPioValue>(
module: PioModule<PioValue>,
) -> Result<PioModule<T>, ValueKindMismatch> {
match module.__try_map_value(T::try_from_pio_value) {
Ok(module) => Ok(module),
Err(module) => Err(ValueKindMismatch {
expected: T::KIND,
module,
}),
}
}
macro_rules! typed_module {
($ty:ty, $variant:ident, $kind:ident) => {
impl private::Sealed for $ty {}
impl FromPioValue for $ty {
const KIND: PioValueKind = PioValueKind::$kind;
fn try_from_pio_value(value: PioValue) -> Result<Self, PioValue> {
match value {
PioValue::$variant(value) => Ok(value),
value => Err(value),
}
}
}
impl From<$ty> for PioValue {
fn from(value: $ty) -> Self {
Self::$variant(value)
}
}
};
}
typed_module!(BalancedNetwork, BalancedNetwork, BalancedNetwork);
typed_module!(
MulticonductorNetwork,
MulticonductorNetwork,
MulticonductorNetwork
);
typed_module!(
powerio_core::TimeSeries<BalancedNetwork>,
BalancedNetworkTimeSeries,
BalancedNetworkTimeSeries
);
typed_module!(
powerio_prob::BalancedOperatingPoints,
BalancedOperatingPointTimeSeries,
BalancedOperatingPointTimeSeries
);
typed_module!(
powerio_core::ScenarioSet<BalancedNetwork>,
BalancedNetworkScenarioSet,
BalancedNetworkScenarioSet
);
typed_module!(
powerio_prob::MulticonductorOperatingPoints,
MulticonductorOperatingPointTimeSeries,
MulticonductorOperatingPointTimeSeries
);
typed_module!(powerio_prob::DcPfInstance, DcPfInstance, DcPfInstance);
typed_module!(powerio_prob::AcPfInstance, AcPfInstance, AcPfInstance);
typed_module!(powerio_prob::DcOpfInstance, DcOpfInstance, DcOpfInstance);
typed_module!(powerio_prob::AcOpfInstance, AcOpfInstance, AcOpfInstance);
typed_module!(powerio_prob::McAcPfInstance, McAcPfInstance, McAcPfInstance);
typed_module!(
powerio_prob::McAcOpfInstance,
McAcOpfInstance,
McAcOpfInstance
);
typed_module!(powerio_prob::AcScucInstance, AcScucInstance, AcScucInstance);
typed_module!(powerio_prob::DcPfSolution, DcPfSolution, DcPfSolution);
typed_module!(powerio_prob::AcPfSolution, AcPfSolution, AcPfSolution);
typed_module!(powerio_prob::DcOpfSolution, DcOpfSolution, DcOpfSolution);
typed_module!(powerio_prob::AcOpfSolution, AcOpfSolution, AcOpfSolution);
typed_module!(powerio_prob::McAcPfSolution, McAcPfSolution, McAcPfSolution);
typed_module!(
powerio_prob::McAcOpfSolution,
McAcOpfSolution,
McAcOpfSolution
);
typed_module!(powerio_prob::AcScucSolution, AcScucSolution, AcScucSolution);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn narrowing_moves_the_value_and_records_without_allocation() {
let network = small_balanced();
let bus_ptr = network.buses().as_ptr();
let module = PioModule::new(PioValue::from(network));
let typed: PioModule<BalancedNetwork> = try_into_typed(module).unwrap();
assert_eq!(typed.value().buses().as_ptr(), bus_ptr);
}
fn small_balanced() -> BalancedNetwork {
use powerio_tx::{Bus, BusId, BusType};
BalancedNetwork::in_memory(
"facade",
100.0,
vec![Bus::new(BusId(1), BusType::Ref, 230.0)],
Vec::new(),
)
}
#[test]
fn a_mismatch_returns_the_dynamic_module() {
let module = PioModule::new(PioValue::from(MulticonductorNetwork::new()));
let error = try_into_typed::<BalancedNetwork>(module).unwrap_err();
assert_eq!(error.expected(), PioValueKind::BalancedNetwork);
assert_eq!(error.actual(), PioValueKind::MulticonductorNetwork);
assert_eq!(
error.to_string(),
"expected `balanced_network`, found `multiconductor_network`"
);
assert_eq!(
error.into_module().value().kind(),
PioValueKind::MulticonductorNetwork
);
}
#[test]
fn kind_strings_are_the_stored_spellings() {
assert_eq!(PioValueKind::BalancedNetwork.as_str(), "balanced_network");
assert_eq!(
PioValueKind::MulticonductorNetwork.as_str(),
"multiconductor_network"
);
}
}