1use crate::{
4 Duration, Identifier, IdentifierError, Unit, interval::IntervalPeriod, program::ProgramId,
5 report::ReportDescriptor, target::Target, values_map::Value,
6};
7use chrono::{DateTime, Utc};
8use iso_currency::Currency;
9use serde::{Deserialize, Serialize};
10use serde_with::{DefaultOnNull, serde_as, skip_serializing_none};
11use std::{
12 fmt::{Display, Formatter},
13 str::FromStr,
14};
15use validator::{Validate, ValidationError};
16
17#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Validate)]
20#[serde(rename_all = "camelCase")]
21pub struct Event {
22 pub id: EventId,
24 #[serde(with = "crate::serde_rfc3339")]
26 pub created_date_time: DateTime<Utc>,
27 #[serde(with = "crate::serde_rfc3339")]
29 pub modification_date_time: DateTime<Utc>,
30 #[serde(flatten)]
31 #[validate(nested)]
32 pub content: EventRequest,
33}
34
35#[skip_serializing_none]
36#[serde_as]
37#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Validate)]
38#[serde(rename_all = "camelCase", tag = "objectType", rename = "EVENT")]
39pub struct EventRequest {
40 #[serde(rename = "programID")]
42 pub program_id: ProgramId,
43 pub event_name: Option<String>,
45 pub duration: Option<Duration>,
47 pub priority: Priority,
49 #[serde(default)]
51 #[serde_as(deserialize_as = "DefaultOnNull")]
52 pub targets: Vec<Target>,
53 pub report_descriptors: Option<Vec<ReportDescriptor>>,
55 pub payload_descriptors: Option<Vec<EventPayloadDescriptor>>,
57 pub interval_period: Option<IntervalPeriod>,
59 #[validate(nested)]
61 pub intervals: Option<Vec<EventInterval>>,
62}
63
64impl EventRequest {
65 pub fn new(program_id: ProgramId) -> Self {
66 Self {
67 program_id,
68 event_name: None,
69 duration: None,
70 priority: Priority::UNSPECIFIED,
71 targets: vec![],
72 report_descriptors: None,
73 payload_descriptors: None,
74 interval_period: None,
75 intervals: None,
76 }
77 }
78
79 pub fn with_event_name(mut self, event_name: impl ToString) -> Self {
80 self.event_name = Some(event_name.to_string());
81 self
82 }
83
84 pub fn with_priority(self, priority: Priority) -> Self {
85 Self { priority, ..self }
86 }
87
88 pub fn with_targets(mut self, targets: Vec<Target>) -> Self {
89 self.targets = targets;
90 self
91 }
92
93 pub fn with_report_descriptors(mut self, report_descriptors: Vec<ReportDescriptor>) -> Self {
94 self.report_descriptors = Some(report_descriptors);
95 self
96 }
97
98 pub fn with_payload_descriptors(
99 mut self,
100 payload_descriptors: Vec<EventPayloadDescriptor>,
101 ) -> Self {
102 self.payload_descriptors = Some(payload_descriptors);
103 self
104 }
105
106 pub fn with_interval_period(mut self, interval_period: IntervalPeriod) -> Self {
107 self.interval_period = Some(interval_period);
108 self
109 }
110
111 pub fn with_intervals(mut self, intervals: Vec<EventInterval>) -> Self {
112 self.intervals = Some(intervals);
113 self
114 }
115}
116
117#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Hash, Eq)]
119pub struct EventId(pub(crate) Identifier);
120
121impl Display for EventId {
122 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
123 write!(f, "{}", self.0)
124 }
125}
126
127impl EventId {
128 pub fn as_str(&self) -> &str {
129 self.0.as_str()
130 }
131}
132
133impl FromStr for EventId {
134 type Err = IdentifierError;
135
136 fn from_str(s: &str) -> Result<Self, Self::Err> {
137 Ok(Self(s.parse()?))
138 }
139}
140
141#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
148#[serde(transparent)]
149pub struct Priority(Option<u32>);
150
151impl Priority {
152 pub const UNSPECIFIED: Self = Self(None);
153
154 pub const MAX: Self = Self(Some(0));
155 pub const MIN: Self = Self::UNSPECIFIED;
156
157 pub const fn new(val: u32) -> Self {
158 Self(Some(val))
159 }
160}
161
162impl PartialOrd for Priority {
163 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
164 Some(self.cmp(other))
165 }
166}
167
168impl Ord for Priority {
169 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
170 use std::cmp::Ordering;
171
172 match (self.0, other.0) {
173 (None, None) => Ordering::Equal,
174 (None, Some(_)) => Ordering::Less,
175 (Some(_), None) => Ordering::Greater,
176 (Some(s), Some(o)) => s.cmp(&o).reverse(),
177 }
178 }
179}
180
181impl From<Option<i64>> for Priority {
182 fn from(value: Option<i64>) -> Self {
183 Self(value.and_then(|i| i.unsigned_abs().try_into().ok()))
184 }
185}
186
187impl From<Priority> for Option<i64> {
188 fn from(value: Priority) -> Self {
189 value.0.map(|u| u.into())
190 }
191}
192
193#[skip_serializing_none]
197#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
198#[serde(rename_all = "camelCase")]
199pub struct EventPayloadDescriptor {
200 pub payload_type: EventType,
204 pub units: Option<Unit>,
206 pub currency: Option<Currency>,
208}
209
210impl EventPayloadDescriptor {
211 pub fn new(payload_type: EventType) -> Self {
212 Self {
213 payload_type,
214 units: None,
215 currency: None,
216 }
217 }
218}
219
220#[skip_serializing_none]
223#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, Validate)]
224#[serde(rename_all = "camelCase")]
225pub struct EventInterval {
226 pub id: i32,
228 pub interval_period: Option<IntervalPeriod>,
230 #[validate(length(min = 1))]
232 pub payloads: Vec<EventValuesMap>,
233}
234
235impl EventInterval {
236 pub fn new(id: i32, payloads: Vec<EventValuesMap>) -> Self {
237 Self {
238 id,
239 interval_period: None,
240 payloads,
241 }
242 }
243}
244
245#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Validate)]
247#[validate(schema(function = "validate_payload"))]
248pub struct EventValuesMap {
249 #[serde(rename = "type")]
251 pub value_type: EventType,
252 pub values: Vec<Value>,
255}
256
257fn validate_payload(payload: &EventValuesMap) -> Result<(), ValidationError> {
263 for value in &payload.values {
264 validate_value(&payload.value_type, value)?
265 }
266 Ok(())
267}
268
269#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
270#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
271pub enum EventType {
272 Simple,
273 Price,
274 ChargeStateSetpoint,
275 DispatchSetpoint,
276 DispatchSetpointRelative,
277 ControlSetpoint,
278 ExportPrice,
279 #[serde(rename = "GHG")]
280 GHG,
281 Curve,
282 #[serde(rename = "OLS")]
283 OLS,
284 ImportCapacitySubscription,
285 ImportCapacityReservation,
286 ImportCapacityReservationFee,
287 ImportCapacityAvailable,
288 ImportCapacityAvailablePrice,
289 ExportCapacitySubscription,
290 ExportCapacityReservation,
291 ExportCapacityReservationFee,
292 ExportCapacityAvailable,
293 ExportCapacityAvailablePrice,
294 ImportCapacityLimit,
295 ExportCapacityLimit,
296 AlertGridEmergency,
297 AlertBlackStart,
298 AlertPossibleOutage,
299 AlertFlexAlert,
300 AlertFire,
301 AlertFreezing,
302 AlertWind,
303 AlertTsunami,
304 AlertAirQuality,
305 AlertOther,
306 #[serde(rename = "CTA2045_REBOOT")]
307 CTA2045Reboot,
308 #[serde(rename = "CTA2045_SET_OVERRIDE_STATUS")]
309 CTA2045SetOverrideStatus,
310 #[serde(untagged)]
311 #[serde(deserialize_with = "crate::string_within_range_inclusive::<1, 128, _>")]
312 Private(String),
313}
314
315fn validate_value(value_type: &EventType, value: &Value) -> Result<(), ValidationError> {
316 match (value_type, value) {
317 (EventType::Simple, Value::Integer(_)) => Ok(()), (EventType::Price, Value::Number(_)) => Ok(()), (EventType::ChargeStateSetpoint, Value::Number(_)) => Ok(()),
320 (EventType::DispatchSetpoint, Value::Number(_)) => Ok(()), (EventType::DispatchSetpointRelative, Value::Number(_)) => Ok(()), (EventType::ControlSetpoint, _) => Ok(()), (EventType::ExportPrice, Value::Number(_)) => Ok(()), (EventType::GHG, Value::Number(_)) => Ok(()), (EventType::Curve, Value::Point(_)) => Ok(()), (EventType::OLS, Value::Number(_)) => Ok(()), (EventType::ImportCapacitySubscription, Value::Number(_)) => Ok(()), (EventType::ImportCapacityReservation, Value::Number(_)) => Ok(()), (EventType::ImportCapacityReservationFee, Value::Number(_)) => Ok(()), (EventType::ImportCapacityAvailable, Value::Number(_)) => Ok(()), (EventType::ImportCapacityAvailablePrice, Value::Number(_)) => Ok(()), (EventType::ExportCapacitySubscription, Value::Number(_)) => Ok(()), (EventType::ExportCapacityReservation, Value::Number(_)) => Ok(()), (EventType::ExportCapacityReservationFee, Value::Number(_)) => Ok(()), (EventType::ExportCapacityAvailable, Value::Number(_)) => Ok(()), (EventType::ExportCapacityAvailablePrice, Value::Number(_)) => Ok(()), (EventType::ImportCapacityLimit, Value::Number(_)) => Ok(()), (EventType::ExportCapacityLimit, Value::Number(_)) => Ok(()), (EventType::AlertGridEmergency, Value::String(_)) => Ok(()), (EventType::AlertBlackStart, Value::String(_)) => Ok(()), (EventType::AlertPossibleOutage, Value::String(_)) => Ok(()), (EventType::AlertFlexAlert, Value::String(_)) => Ok(()), (EventType::AlertFire, Value::String(_)) => Ok(()), (EventType::AlertFreezing, Value::String(_)) => Ok(()), (EventType::AlertWind, Value::String(_)) => Ok(()), (EventType::AlertTsunami, Value::String(_)) => Ok(()), (EventType::AlertAirQuality, Value::String(_)) => Ok(()), (EventType::AlertOther, Value::String(_)) => Ok(()), (EventType::CTA2045Reboot, Value::Integer(_)) => Ok(()), (EventType::CTA2045SetOverrideStatus, Value::Integer(_)) => Ok(()), (EventType::Private(_), _) => Ok(()), (value_type, value) => Err(validate_value_error(value_type, value)),
353 }
354}
355
356fn validate_value_error(value_type: &EventType, value: &Value) -> ValidationError {
357 let cow = format!("value {value:?} must match the given type {value_type:?}").into();
358 ValidationError::new("values must match the given type").with_message(cow)
359}
360
361#[cfg(test)]
362mod tests {
363 use crate::{Duration, values_map::Value};
364 use std::borrow::Cow;
365
366 use super::*;
367
368 #[test]
369 fn priority_order() {
370 assert_eq!(Priority::MAX, Priority::new(0));
371 assert!(Priority::MAX > Priority::MIN);
372 assert_eq!(Priority::MIN, Priority::UNSPECIFIED);
373 assert!(Priority::new(5) > Priority::UNSPECIFIED);
374 assert!(Priority::new(5) > Priority::new(6));
375 assert!(Priority::new(u32::MAX) > Priority::UNSPECIFIED);
376 }
377
378 #[test]
379 fn test_event_serialization() {
380 assert_eq!(
381 serde_json::to_string(&EventType::Simple).unwrap(),
382 r#""SIMPLE""#
383 );
384 assert_eq!(
385 serde_json::to_string(&EventType::CTA2045Reboot).unwrap(),
386 r#""CTA2045_REBOOT""#
387 );
388 assert_eq!(
389 serde_json::from_str::<EventType>(r#""GHG""#).unwrap(),
390 EventType::GHG
391 );
392 assert_eq!(
393 serde_json::from_str::<EventType>(r#""something else""#).unwrap(),
394 EventType::Private(String::from("something else"))
395 );
396
397 assert!(serde_json::from_str::<EventType>(r#""""#).is_err());
398 assert!(serde_json::from_str::<EventType>(&format!("\"{}\"", "x".repeat(129))).is_err());
399 }
400
401 #[test]
402 fn parse_minimal() {
403 let example = r#"{"programID":"foo"}"#;
404 assert_eq!(
405 serde_json::from_str::<EventRequest>(example).unwrap(),
406 EventRequest {
407 program_id: ProgramId("foo".parse().unwrap()),
408 event_name: None,
409 duration: None,
410 priority: Priority::MIN,
411 targets: vec![],
412 report_descriptors: None,
413 payload_descriptors: None,
414 interval_period: None,
415 intervals: None,
416 }
417 );
418 }
419
420 #[test]
421 fn example_parses() {
422 let example = r#"[{
423 "id": "object-999-foo",
424 "createdDateTime": "2023-06-15T09:30:00Z",
425 "modificationDateTime": "2023-06-15T09:30:00Z",
426 "objectType": "EVENT",
427 "programID": "object-999",
428 "eventName": "price event 11-18-2022",
429 "duration": "PT1H",
430 "priority": 0,
431 "targets": null,
432 "reportDescriptors": null,
433 "payloadDescriptors": null,
434 "intervalPeriod": {
435 "start": "2023-06-15T09:30:00Z",
436 "duration": "PT1H",
437 "randomizeStart": "PT1H"
438 },
439 "intervals": [
440 {
441 "id": 0,
442 "intervalPeriod": {
443 "start": "2023-06-15T09:30:00Z",
444 "duration": "PT1H",
445 "randomizeStart": "PT1H"
446 },
447 "payloads": [
448 {
449 "type": "PRICE",
450 "values": [
451 0.17
452 ]
453 }
454 ]
455 }
456 ]
457 }]"#;
458
459 let expected = Event {
460 id: EventId("object-999-foo".parse().unwrap()),
461 created_date_time: "2023-06-15T09:30:00Z".parse().unwrap(),
462 modification_date_time: "2023-06-15T09:30:00Z".parse().unwrap(),
463 content: EventRequest {
464 program_id: ProgramId("object-999".parse().unwrap()),
465 event_name: Some("price event 11-18-2022".into()),
466 duration: Some(Duration::PT1H),
467 priority: Priority::MAX,
468 targets: Default::default(),
469 report_descriptors: None,
470 payload_descriptors: None,
471 interval_period: Some(IntervalPeriod {
472 start: "2023-06-15T09:30:00Z".parse().unwrap(),
473 duration: Some(Duration::PT1H),
474 randomize_start: Some(Duration::PT1H),
475 }),
476 intervals: Some(vec![EventInterval {
477 id: 0,
478 interval_period: Some(IntervalPeriod {
479 start: "2023-06-15T09:30:00Z".parse().unwrap(),
480 duration: Some(Duration::PT1H),
481 randomize_start: Some(Duration::PT1H),
482 }),
483 payloads: vec![EventValuesMap {
484 value_type: EventType::Price,
485 values: vec![Value::Number(0.17)],
486 }],
487 }]),
488 },
489 };
490
491 assert_eq!(
492 serde_json::from_str::<Vec<Event>>(example).unwrap()[0],
493 expected
494 );
495 }
496
497 #[test]
498 fn test_currency() {
499 let example = r#"{"payloadType":"SIMPLE","currency":"EUR"}"#;
501
502 let expected = EventPayloadDescriptor {
503 payload_type: EventType::Simple,
504 units: None,
505 currency: Some(Currency::EUR),
506 };
507
508 assert_eq!(
509 serde_json::from_str::<EventPayloadDescriptor>(example).unwrap(),
510 expected
511 );
512
513 let source = EventPayloadDescriptor {
515 payload_type: EventType::Price,
516 units: Some(Unit::Volts),
517 currency: Some(Currency::USD),
518 };
519
520 let serialized = serde_json::to_string(&source).unwrap();
521
522 assert_eq!(
523 source,
524 serde_json::from_str::<EventPayloadDescriptor>(&serialized).unwrap()
525 );
526 }
527
528 #[test]
529 fn test_validate_value_positive() {
530 let input = r#"{"type":"SIMPLE","values":[1]}"#;
531 let expected = Ok(());
532 let actual = serde_json::from_str::<EventValuesMap>(input)
533 .unwrap()
534 .validate();
535 assert_eq!(actual, expected);
536 }
537
538 #[test]
539 fn validate_private_value() {
540 let input = r#"{"type":"WHATEVER","values":["Private types must accept all values"]}"#;
541 let expected = Ok(());
542 let actual = serde_json::from_str::<EventValuesMap>(input)
543 .unwrap()
544 .validate();
545 assert_eq!(actual, expected);
546
547 let input = r#"{"type":"WHATEVER","values":[1]}"#;
548 let expected = Ok(());
549 let actual = serde_json::from_str::<EventValuesMap>(input)
550 .unwrap()
551 .validate();
552 assert_eq!(actual, expected);
553
554 let input = r#"{"type":"WHATEVER","values":[{"x": 1, "y": 3}]}"#;
555 let expected = Ok(());
556 let actual = serde_json::from_str::<EventValuesMap>(input)
557 .unwrap()
558 .validate();
559 assert_eq!(actual, expected);
560 }
561
562 #[test]
563 fn test_validate_value_negative() {
564 let input = r#"{"type":"SIMPLE","values":["string"]}"#;
565 let expected = {
566 use std::collections::HashMap;
567 use validator::{ValidationErrors, ValidationErrorsKind};
568 let mut hash_map = HashMap::new();
569 let validation_errors_kind = {
570 let value = Value::String("string".to_string());
571 ValidationErrorsKind::Field(vec![validate_value_error(&EventType::Simple, &value)])
572 };
573 hash_map.insert(Cow::from("__all__"), validation_errors_kind);
574 Err(ValidationErrors(hash_map))
575 };
576 let actual = serde_json::from_str::<EventValuesMap>(input)
577 .unwrap()
578 .validate();
579 assert_eq!(actual, expected);
580 }
581}