1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! A roaming hub: routing, broadcast push, open routing, GET All, and version bridging.
//!
//! # The four routing arrangements
//!
//! A hub can tell which one it is looking at from the headers and the method alone, which is what
//! [`Forwardable::scenario`] does:
//!
//! | The `OCPI-to-*` headers say | Method | It is |
//! |---|---|---|
//! | another party | any | [`Direct`](crate::transport::RoutingScenario::Direct) — relay it |
//! | the hub itself | `GET` on a Sender interface | [`GetAllViaHub`](crate::transport::RoutingScenario::GetAllViaHub) — merge every party's objects |
//! | the hub itself | a write on a Receiver interface | [`BroadcastPush`](crate::transport::RoutingScenario::BroadcastPush) — fan out to the opposite roles |
//! | nothing | any | [`OpenRoutingRequest`](crate::transport::RoutingScenario::OpenRoutingRequest) — decide from the content |
//! | the hub itself | anything else | refused: [`OcpiError::NotRoutable`](crate::transport::OcpiError::NotRoutable), a `2001` |
//!
//! Addressing the hub is the one ambiguous case, and two of its four combinations are not
//! scenarios at all: a `GET` on a Receiver interface is not a Broadcast Push, because *"GET SHALL
//! NOT be used in combination with Broadcast Push"*, and a write on a Sender interface is neither
//! a push to the connected parties nor a read to merge. Guessing the nearest scenario would mean
//! a hub quietly broadcasting a read, so [`Forwardable::scenario`] refuses, with the advice the
//! specification itself gives: omit the `OCPI-to-` headers and make it an Open Routing Request.
//!
//! # The rules the hub must not break
//!
//! * **A new `X-Request-ID`, the same `X-Correlation-ID`.** [`RequestIds::forwarded`](crate::transport::RequestIds::forwarded).
//! * **`last_updated` is never touched.** *"When OCPI Objects are sent via Hubs, the
//! `last_updated` fields SHALL NOT be updated by the Hub."* Nothing in this module writes it.
//! * **`GET` is never broadcast.** *"GET SHALL NOT be used in combination with Broadcast Push."*
//! * **Configuration modules are never routed.** `credentials`, `versions` and `hubclientinfo`
//! are platform-to-hub conversations.
//! * **Vendor data survives.** Because every object keeps its unknown fields in
//! [`Extensions`](crate::types::Extensions) and every `OpenEnum` keeps values it does not know,
//! a hub built on this crate forwards an extension it has never seen without damaging it. That
//! is what OCPI 2.3.0's extensibility chapter asks for, and it is the single most common way a
//! hub loses data.
//!
//! # Version bridging
//!
//! A 2.2.1 CPO and a 2.3.0 eMSP talk through the hub without either of them knowing: [`Forwarder`]
//! translates the request body into the version the receiving platform speaks, translates the
//! response back, and appends what the crossing cost to the `status_message` so the requesting
//! party can see it. That is [`convert`](crate::convert) doing the work and
//! [`Lossy`](crate::convert::Lossy) doing the reporting; no object is ever handed to a party in a
//! version it did not ask for, and nothing is dropped silently.
//!
//! Only the 2.2.1 ↔ 2.3.0 crossing has conversions today. A message between two versions this
//! build cannot translate is **refused** by default rather than relayed — see [`Unbridgeable`] for
//! the reasoning and for how to relay it anyway. [`bridge`] exposes the same classification for a
//! hub that wants to translate an object itself.
//!
//! Spec: 2.3.0 §transport_and_format_message_routing, §status_codes_4xxx_hub_errors
pub use ;
pub use ;
use crateVersionNumber;
/// What a hub must do to a message crossing between two OCPI versions.
/// What the hub must do to carry a message from `sender` to `receiver`.
///
/// ```
/// use ocpi_kit::hub::{bridge, Bridge};
/// use ocpi_kit::VersionNumber;
///
/// assert_eq!(bridge(&VersionNumber::V2_3_0, &VersionNumber::V2_3_0), Bridge::Passthrough);
/// assert_eq!(bridge(&VersionNumber::V2_2_1, &VersionNumber::V2_3_0), Bridge::Upgrade);
/// assert_eq!(bridge(&VersionNumber::V2_3_0, &VersionNumber::V2_2_1), Bridge::Downgrade);
/// assert_eq!(bridge(&VersionNumber::V2_3_0, &"3.0".into()), Bridge::Unsupported);
/// ```