1use std::fmt;
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use time::OffsetDateTime;
5use time::format_description::well_known::Rfc3339;
6
7use crate::RedispatchXmlError;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub struct DocumentId(String);
16
17impl DocumentId {
18 #[must_use = "this returns the new DocumentId, discarding it is likely a mistake"]
21 pub fn new(s: impl Into<String>) -> Result<Self, RedispatchXmlError> {
22 let s = s.into();
23 if s.is_empty() || s.len() > 35 {
24 return Err(RedispatchXmlError::InvalidDocumentId(s));
25 }
26 Ok(Self(s))
27 }
28
29 pub fn as_str(&self) -> &str {
31 &self.0
32 }
33}
34
35impl AsRef<str> for DocumentId {
36 fn as_ref(&self) -> &str {
37 &self.0
38 }
39}
40
41impl TryFrom<String> for DocumentId {
42 type Error = RedispatchXmlError;
43 fn try_from(s: String) -> Result<Self, Self::Error> {
44 Self::new(s)
45 }
46}
47
48impl TryFrom<&str> for DocumentId {
49 type Error = RedispatchXmlError;
50 fn try_from(s: &str) -> Result<Self, Self::Error> {
51 Self::new(s)
52 }
53}
54
55impl fmt::Display for DocumentId {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 self.0.fmt(f)
58 }
59}
60
61impl<'de> Deserialize<'de> for DocumentId {
62 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
63 let s = String::deserialize(d)?;
64 Self::new(s).map_err(serde::de::Error::custom)
65 }
66}
67
68impl Serialize for DocumentId {
69 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
70 self.0.serialize(s)
71 }
72}
73
74pub type Mrid = DocumentId;
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
85pub struct DocumentVersion(u16);
86
87impl DocumentVersion {
88 #[must_use = "this returns the new DocumentVersion, discarding it is likely a mistake"]
90 pub fn new(v: u32) -> Result<Self, RedispatchXmlError> {
91 if v == 0 || v > 999 {
92 return Err(RedispatchXmlError::InvalidDocumentVersion(v));
93 }
94 Ok(Self(v as u16))
95 }
96
97 pub fn get(self) -> u16 {
99 self.0
100 }
101}
102
103impl fmt::Display for DocumentVersion {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 self.0.fmt(f)
106 }
107}
108
109impl<'de> Deserialize<'de> for DocumentVersion {
110 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
111 let v: u32 = Deserialize::deserialize(d)?;
113 Self::new(v).map_err(serde::de::Error::custom)
114 }
115}
116
117impl Serialize for DocumentVersion {
118 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
119 self.0.serialize(s)
120 }
121}
122
123pub type RevisionNumber = DocumentVersion;
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
133pub struct UtcDateTime(OffsetDateTime);
134
135impl UtcDateTime {
136 #[must_use = "this returns the new UtcDateTime, discarding it is likely a mistake"]
139 pub fn new(dt: OffsetDateTime) -> Result<Self, RedispatchXmlError> {
140 if dt.offset() != time::UtcOffset::UTC {
141 return Err(RedispatchXmlError::InvalidTimestamp(dt.to_string()));
142 }
143 Ok(Self(dt))
144 }
145
146 pub fn inner(self) -> OffsetDateTime {
148 self.0
149 }
150}
151
152impl<'de> Deserialize<'de> for UtcDateTime {
153 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
154 let s = String::deserialize(d)?;
155 let dt = OffsetDateTime::parse(&s, &Rfc3339)
156 .map_err(|_| serde::de::Error::custom(format!("invalid UTC timestamp: {s:?}")))?;
157 if dt.offset() != time::UtcOffset::UTC {
158 return Err(serde::de::Error::custom(format!(
159 "timestamp must use UTC (Z suffix): {s:?}"
160 )));
161 }
162 Ok(UtcDateTime(dt))
163 }
164}
165
166impl Serialize for UtcDateTime {
167 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
168 self.0
170 .format(&Rfc3339)
171 .map_err(serde::ser::Error::custom)?
172 .serialize(s)
173 }
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
182pub struct UtcMinuteDateTime(OffsetDateTime);
183
184impl UtcMinuteDateTime {
185 #[must_use = "this returns the new UtcMinuteDateTime, discarding it is likely a mistake"]
187 pub fn new(dt: OffsetDateTime) -> Result<Self, RedispatchXmlError> {
188 if dt.offset() != time::UtcOffset::UTC {
189 return Err(RedispatchXmlError::InvalidTimestamp(dt.to_string()));
190 }
191 Ok(Self(dt))
192 }
193
194 pub fn inner(self) -> OffsetDateTime {
196 self.0
197 }
198}
199
200const MINUTE_FMT: &[time::format_description::BorrowedFormatItem<'static>] =
201 time::macros::format_description!("[year]-[month]-[day]T[hour]:[minute]Z");
202
203impl<'de> Deserialize<'de> for UtcMinuteDateTime {
204 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
205 let s = String::deserialize(d)?;
206 let naive = time::PrimitiveDateTime::parse(&s, MINUTE_FMT).map_err(|_| {
207 serde::de::Error::custom(format!("invalid UTC minute timestamp: {s:?}"))
208 })?;
209 Ok(UtcMinuteDateTime(naive.assume_offset(time::UtcOffset::UTC)))
210 }
211}
212
213impl Serialize for UtcMinuteDateTime {
214 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
215 self.0
216 .format(MINUTE_FMT)
217 .map_err(serde::ser::Error::custom)?
218 .serialize(s)
219 }
220}
221
222#[derive(Debug, Clone, PartialEq, Eq)]
228pub struct TimeInterval {
229 pub start: OffsetDateTime,
231 pub end: OffsetDateTime,
233}
234
235impl TimeInterval {
236 #[must_use = "this returns the new TimeInterval, discarding it is likely a mistake"]
239 pub fn new(start: OffsetDateTime, end: OffsetDateTime) -> Result<Self, RedispatchXmlError> {
240 if start.offset() != time::UtcOffset::UTC || end.offset() != time::UtcOffset::UTC {
241 return Err(RedispatchXmlError::InvalidTimeInterval(
242 "timestamps must be UTC".into(),
243 ));
244 }
245 if start >= end {
246 return Err(RedispatchXmlError::InvalidTimeInterval(
247 "start must be before end".into(),
248 ));
249 }
250 Ok(Self { start, end })
251 }
252}
253
254impl<'de> Deserialize<'de> for TimeInterval {
255 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
256 let s = String::deserialize(d)?;
257 let (start_str, end_str) = s.split_once('/').ok_or_else(|| {
258 serde::de::Error::custom(format!("invalid time interval {s:?}: missing '/'"))
259 })?;
260 let start_naive = time::PrimitiveDateTime::parse(start_str, MINUTE_FMT).map_err(|_| {
261 serde::de::Error::custom(format!("invalid interval start {start_str:?}"))
262 })?;
263 let end_naive = time::PrimitiveDateTime::parse(end_str, MINUTE_FMT)
264 .map_err(|_| serde::de::Error::custom(format!("invalid interval end {end_str:?}")))?;
265 Ok(TimeInterval {
266 start: start_naive.assume_offset(time::UtcOffset::UTC),
267 end: end_naive.assume_offset(time::UtcOffset::UTC),
268 })
269 }
270}
271
272impl Serialize for TimeInterval {
273 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
274 let start = self
275 .start
276 .format(MINUTE_FMT)
277 .map_err(serde::ser::Error::custom)?;
278 let end = self
279 .end
280 .format(MINUTE_FMT)
281 .map_err(serde::ser::Error::custom)?;
282 format!("{start}/{end}").serialize(s)
283 }
284}
285
286impl fmt::Display for TimeInterval {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 let start = self.start.format(MINUTE_FMT).map_err(|_| fmt::Error)?;
289 let end = self.end.format(MINUTE_FMT).map_err(|_| fmt::Error)?;
290 write!(f, "{start}/{end}")
291 }
292}
293
294#[derive(Debug, Clone, PartialEq, Eq, Hash)]
298pub struct MarketParticipantId(String);
299
300impl MarketParticipantId {
301 #[must_use = "this returns the new MarketParticipantId, discarding it is likely a mistake"]
303 pub fn new(s: impl Into<String>) -> Result<Self, RedispatchXmlError> {
304 let s = s.into();
305 if s.len() != 13 || !s.chars().all(|c| c.is_ascii_digit()) {
306 return Err(RedispatchXmlError::InvalidMarketParticipantId(s));
307 }
308 Ok(Self(s))
309 }
310
311 pub fn as_str(&self) -> &str {
313 &self.0
314 }
315}
316
317impl AsRef<str> for MarketParticipantId {
318 fn as_ref(&self) -> &str {
319 &self.0
320 }
321}
322
323impl TryFrom<String> for MarketParticipantId {
324 type Error = RedispatchXmlError;
325 fn try_from(s: String) -> Result<Self, Self::Error> {
326 Self::new(s)
327 }
328}
329
330impl TryFrom<&str> for MarketParticipantId {
331 type Error = RedispatchXmlError;
332 fn try_from(s: &str) -> Result<Self, Self::Error> {
333 Self::new(s)
334 }
335}
336
337impl fmt::Display for MarketParticipantId {
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 self.0.fmt(f)
340 }
341}
342
343impl<'de> Deserialize<'de> for MarketParticipantId {
344 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
345 let s = String::deserialize(d)?;
346 Self::new(s).map_err(serde::de::Error::custom)
347 }
348}
349
350impl Serialize for MarketParticipantId {
351 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
352 self.0.serialize(s)
353 }
354}
355
356#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
361pub struct Decimal3(f64);
362
363impl Decimal3 {
364 #[must_use = "this returns the new Decimal3, discarding it is likely a mistake"]
371 pub fn new(v: f64) -> Result<Self, RedispatchXmlError> {
372 if v < 0.0 {
373 return Err(RedispatchXmlError::StructuralError(format!(
374 "Decimal3 value {v} must be ≥ 0"
375 )));
376 }
377 Ok(Self(v))
378 }
379
380 pub fn value(self) -> f64 {
382 self.0
383 }
384}
385
386impl<'de> Deserialize<'de> for Decimal3 {
387 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
388 let v: f64 = Deserialize::deserialize(d)?;
390 Self::new(v).map_err(serde::de::Error::custom)
391 }
392}
393
394impl Serialize for Decimal3 {
395 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
396 format!("{:.3}", self.0).serialize(s)
399 }
400}
401
402impl fmt::Display for Decimal3 {
403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404 write!(f, "{:.3}", self.0)
405 }
406}
407
408#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
412pub enum CodingScheme {
413 #[serde(rename = "A10")]
416 Gs1,
417 #[serde(rename = "NDE")]
419 Nde,
420 #[serde(rename = "A01")]
422 Eic,
423}
424
425#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
429#[non_exhaustive]
430pub enum MeasureUnit {
431 #[serde(rename = "MAW")]
433 Megawatt,
434 #[serde(rename = "P1")]
436 Percent,
437}
438
439#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
443#[non_exhaustive]
444pub enum Direction {
445 #[serde(rename = "A01")]
447 Up,
448 #[serde(rename = "A02")]
450 Down,
451}
452
453#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
457#[non_exhaustive]
458pub enum MarketRoleType {
459 #[serde(rename = "A08")]
461 BalanceResponsibleParty,
462 #[serde(rename = "A18")]
464 GridOperator,
465 #[serde(rename = "A21")]
467 Producer,
468 #[serde(rename = "A27")]
470 ResourceProvider,
471 #[serde(rename = "A39")]
473 DataProvider,
474 #[serde(rename = "Z01")]
476 Supplier,
477}
478
479#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
484#[non_exhaustive]
485pub enum ControlZone {
486 #[serde(rename = "10YDE-ENBW-----N")]
488 TransnetBw,
489 #[serde(rename = "10YDE-EON------1")]
491 TennetDe,
492 #[serde(rename = "10YDE-RWENET---I")]
494 Amprion,
495 #[serde(rename = "10YDE-VE-------2")]
497 FiftyHertz,
498 #[serde(rename = "10YFLENSBURG---3")]
500 Flensburg,
501 #[serde(rename = "11YRBAHNSTROM--P")]
503 Bahnstrom,
504}