ocpi_kit/v2_3_0/
hub_client_info.rs1use serde::{Deserialize, Serialize};
11
12use crate::ocpi_enum;
13use crate::types::validate_fields;
14use crate::types::{CountryCode, DateTime, Extensions, PartyId, PartyRef, Validate, Validator};
15
16use super::types::Role;
17
18#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
22#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
23pub struct ClientInfo {
24 pub party_id: PartyId,
26 pub country_code: CountryCode,
28 pub role: Role,
30 pub status: ConnectionStatus,
32 pub last_updated: DateTime,
34 #[serde(flatten, default, skip_serializing_if = "Extensions::is_empty")]
36 pub extensions: Extensions,
37}
38
39impl ClientInfo {
40 #[must_use]
42 pub fn new(party: PartyRef, role: Role, status: ConnectionStatus, last_updated: DateTime) -> Self {
43 Self {
44 party_id: party.party_id,
45 country_code: party.country_code,
46 role,
47 status,
48 last_updated,
49 extensions: Extensions::new(),
50 }
51 }
52
53 #[must_use]
55 pub fn party(&self) -> PartyRef {
56 PartyRef { country_code: self.country_code.clone(), party_id: self.party_id.clone() }
57 }
58
59 #[must_use]
61 pub fn is_reachable(&self) -> bool {
62 self.status == ConnectionStatus::Connected
63 }
64}
65
66impl Validate for ClientInfo {
67 fn validate_in(&self, v: &mut Validator) {
68 validate_fields!(self, v, party_id, country_code, role, status, last_updated);
69 }
70}
71
72ocpi_enum! {
73 pub enum ConnectionStatus {
77 Connected = "CONNECTED",
79 Offline = "OFFLINE",
81 Planned = "PLANNED",
83 Suspended = "SUSPENDED",
85 }
86}
87
88impl ConnectionStatus {
89 #[must_use]
94 pub const fn should_poll(self) -> bool {
95 matches!(self, Self::Connected | Self::Offline)
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn only_connected_parties_are_reachable() {
105 let info = ClientInfo::new(
106 PartyRef::new("NL", "TNM").unwrap(),
107 Role::Cpo,
108 ConnectionStatus::Offline,
109 "2019-06-24T12:39:09Z".parse().unwrap(),
110 );
111 assert!(!info.is_reachable());
112 assert!(info.status.should_poll(), "an offline party may come back");
113 assert!(!ConnectionStatus::Suspended.should_poll());
114 assert!(!ConnectionStatus::Planned.should_poll());
115 }
116
117 #[test]
118 fn round_trips_the_spec_shape() {
119 let json = r#"{"party_id":"TNM","country_code":"NL","role":"CPO","status":"CONNECTED","last_updated":"2019-06-24T12:39:09Z"}"#;
120 let info: ClientInfo = serde_json::from_str(json).unwrap();
121 assert_eq!(info.party(), PartyRef::new("NL", "TNM").unwrap());
122 assert_eq!(serde_json::to_string(&info).unwrap(), json);
123 }
124}