#![allow(clippy::doc_markdown)]
use serde::{Deserialize, Serialize};
pub use rubo4e::identifiers::{
EicCode, MaloId, MarktpartnerId, MeloId, NeloId, ObisCode, SrId, TrId,
};
#[must_use]
#[inline]
pub fn nad_agency_code(id: &MarktpartnerId) -> &'static str {
id.nad_agency_code()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Sparte {
Strom,
Gas,
}
impl std::fmt::Display for Sparte {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Strom => write!(f, "STROM"),
Self::Gas => write!(f, "GAS"),
}
}
}
impl std::str::FromStr for Sparte {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_uppercase().as_str() {
"STROM" => Ok(Self::Strom),
"GAS" => Ok(Self::Gas),
other => Err(format!("unknown Sparte '{other}'; expected STROM or GAS")),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Lokationstyp {
Malo,
Melo,
Nelo,
Sr,
Tr,
}
impl Lokationstyp {
pub const VARIANTS: &'static [&'static str] = &["MALO", "MELO", "NELO", "SR", "TR"];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Malo => "MALO",
Self::Melo => "MELO",
Self::Nelo => "NELO",
Self::Sr => "SR",
Self::Tr => "TR",
}
}
}
impl From<Lokationstyp> for &'static str {
fn from(t: Lokationstyp) -> Self {
t.as_str()
}
}
impl std::fmt::Display for Lokationstyp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl std::str::FromStr for Lokationstyp {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_uppercase().as_str() {
"MALO" => Ok(Self::Malo),
"MELO" => Ok(Self::Melo),
"NELO" => Ok(Self::Nelo),
"SR" => Ok(Self::Sr),
"TR" => Ok(Self::Tr),
other => Err(format!(
"unknown Lokationstyp '{other}'; expected one of {:?}",
Self::VARIANTS
)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ProcessStatus {
Running,
Completed,
Failed,
}
impl std::fmt::Display for ProcessStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Running => write!(f, "RUNNING"),
Self::Completed => write!(f, "COMPLETED"),
Self::Failed => write!(f, "FAILED"),
}
}
}
impl std::str::FromStr for ProcessStatus {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"RUNNING" => Ok(Self::Running),
"COMPLETED" => Ok(Self::Completed),
"FAILED" => Ok(Self::Failed),
other => Err(format!("unknown process status: {other}")),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn malo_id_valid() {
assert!("51238696012".parse::<MaloId>().is_ok());
}
#[test]
fn malo_id_wrong_checksum() {
assert!("51238696782".parse::<MaloId>().is_err());
}
#[test]
fn malo_id_too_short() {
assert!("1234567890".parse::<MaloId>().is_err());
}
#[test]
fn melo_id_valid() {
let id = "DE0001234567890123456789012345678";
assert!(id.parse::<MeloId>().is_ok());
}
#[test]
fn melo_id_invalid_prefix() {
assert!(
"1X0001234567890123456789012345678"
.parse::<MeloId>()
.is_err()
);
}
#[test]
fn melo_id_invalid_prefix_lowercase() {
assert!(
"de0001234567890123456789012345678"
.parse::<MeloId>()
.is_err()
);
}
#[test]
fn melo_id_invalid_length() {
assert!("DE00012345678".parse::<MeloId>().is_err());
}
#[test]
fn melo_id_non_de_prefix_valid() {
assert!(
"AT0001234567890123456789012345678"
.parse::<MeloId>()
.is_ok()
);
}
#[test]
fn marktpartner_id_gs1_gln() {
let id: MarktpartnerId = "1234567890128".parse().unwrap();
assert_eq!(nad_agency_code(&id), "9");
}
#[test]
fn marktpartner_id_bdew() {
let id: MarktpartnerId = "9900357000004".parse().unwrap();
assert_eq!(nad_agency_code(&id), "293");
}
#[test]
fn marktpartner_id_dvgw() {
let id: MarktpartnerId = "9800001000003".parse().unwrap();
assert_eq!(nad_agency_code(&id), "332");
}
#[test]
fn marktpartner_id_invalid_length() {
assert!("123".parse::<MarktpartnerId>().is_err());
}
}