use http::Method;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::convert::wire::{BridgeError, ObjectKind};
use crate::transport::{
OcpiError, OcpiRequest, Page, PageQuery, Patch, ReceiverEndpoint, RequestIds, RoutingHeaders,
SenderEndpoint,
};
use crate::types::{PartyRef, Url, Validate};
use crate::v2_3_0::tokens::TokenType;
use crate::{InterfaceRole, ModuleId};
use super::http::{Transport, check_outgoing};
use super::paging::PageStream;
use super::peer::Peer;
#[derive(Clone, Debug)]
pub struct ModuleClient<'a> {
transport: &'a Transport,
peer: &'a Peer,
module: ModuleId,
from: PartyRef,
to: Option<PartyRef>,
}
impl<'a> ModuleClient<'a> {
#[must_use]
pub fn new(transport: &'a Transport, peer: &'a Peer, module: ModuleId, from: PartyRef) -> Self {
let to = peer.default_party().cloned();
Self { transport, peer, module, from, to }
}
#[must_use]
pub fn to(mut self, party: PartyRef) -> Self {
self.to = Some(party);
self
}
#[must_use]
pub fn open_routing(mut self) -> Self {
self.to = None;
self
}
#[must_use]
pub const fn peer(&self) -> &Peer {
self.peer
}
#[must_use]
pub fn sender_endpoint(&self) -> Option<SenderEndpoint> {
self.peer.sender(&self.module)
}
#[must_use]
pub fn receiver_endpoint(&self) -> Option<ReceiverEndpoint> {
self.peer.receiver(&self.module)
}
fn routing(&self) -> RoutingHeaders {
RoutingHeaders { to: self.to.clone(), from: self.from.clone() }
}
fn request(&self, method: Method, url: Url) -> OcpiRequest {
OcpiRequest::new(method, url, self.module.clone()).routed(self.routing())
}
fn missing(&self, role: InterfaceRole) -> OcpiError {
OcpiError::NotFound(format!(
"the peer does not implement the {} interface of the {} module",
role, self.module
))
}
pub async fn get<T: DeserializeOwned>(&self, url: Url) -> Result<T, OcpiError> {
let request = self.request(Method::GET, url);
self.transport.send(&request, self.peer.token(), self.peer.quirks()).await
}
pub async fn get_page<T: DeserializeOwned>(&self, url: Url) -> Result<Page<T>, OcpiError> {
let request = self.request(Method::GET, url);
self.transport.send_page(&request, self.peer.token(), self.peer.quirks()).await
}
pub async fn put<T: Serialize + Validate>(&self, url: Url, body: &T) -> Result<(), OcpiError> {
check_outgoing(body, self.transport.config())?;
let request = self.request(Method::PUT, url).with_body(body)?;
let (response, _) = self
.transport
.send_with_headers::<serde_json::Value>(&request, self.peer.token(), self.peer.quirks())
.await?;
if response.is_success() { Ok(()) } else { Err(response.into_result().unwrap_err()) }
}
pub async fn post<B: Serialize + Validate, T: DeserializeOwned>(
&self,
url: Url,
body: &B,
) -> Result<T, OcpiError> {
check_outgoing(body, self.transport.config())?;
let request = self.request(Method::POST, url).with_body(body)?;
self.transport.send(&request, self.peer.token(), self.peer.quirks()).await
}
pub async fn patch<T>(&self, url: Url, patch: &Patch<T>) -> Result<(), OcpiError> {
if patch.last_updated().is_none() {
return Err(OcpiError::Decode {
path: "/last_updated".to_owned(),
message: "a PATCH must carry `last_updated`".to_owned(),
});
}
let request = self.request(Method::PATCH, url).with_body(patch.as_value())?;
let (response, _) = self
.transport
.send_with_headers::<serde_json::Value>(&request, self.peer.token(), self.peer.quirks())
.await?;
if response.is_success() { Ok(()) } else { Err(response.into_result().unwrap_err()) }
}
pub async fn delete(&self, url: Url) -> Result<(), OcpiError> {
let request = self.request(Method::DELETE, url);
let (response, _) = self
.transport
.send_with_headers::<serde_json::Value>(&request, self.peer.token(), self.peer.quirks())
.await?;
if response.is_success() { Ok(()) } else { Err(response.into_result().unwrap_err()) }
}
fn foreign_version(&self) -> Option<&crate::VersionNumber> {
let version = self.peer.version();
(*version != crate::CANONICAL_VERSION).then_some(version)
}
pub async fn get_bridged<T: DeserializeOwned>(&self, url: Url, kind: ObjectKind) -> Result<T, OcpiError> {
let Some(theirs) = self.foreign_version() else { return self.get(url).await };
let value: serde_json::Value = self.get(url).await?;
let converted =
kind.bridge(theirs, &crate::CANONICAL_VERSION, value).map_err(|e| bridge_error(e, kind))?;
decode(converted.value)
}
pub async fn put_bridged<T: Serialize + Validate>(
&self,
url: Url,
body: &T,
kind: ObjectKind,
) -> Result<(), OcpiError> {
check_outgoing(body, self.transport.config())?;
let Some(value) = self.for_peer(body, kind)? else { return self.put(url, body).await };
let request = self.request(Method::PUT, url).with_body(&value)?;
self.expect_success(request).await
}
pub async fn post_bridged<B: Serialize + Validate, T: DeserializeOwned>(
&self,
url: Url,
body: &B,
request_kind: Option<ObjectKind>,
response_kind: Option<ObjectKind>,
) -> Result<T, OcpiError> {
check_outgoing(body, self.transport.config())?;
let Some(theirs) = self.foreign_version().cloned() else {
return self.post(url, body).await;
};
let request = match request_kind.and_then(|k| self.for_peer(body, k).transpose()) {
Some(value) => self.request(Method::POST, url).with_body(&value?)?,
None => self.request(Method::POST, url).with_body(body)?,
};
let answer: serde_json::Value =
self.transport.send(&request, self.peer.token(), self.peer.quirks()).await?;
let Some(kind) = response_kind else { return decode(answer) };
let converted =
kind.bridge(&theirs, &crate::CANONICAL_VERSION, answer).map_err(|e| bridge_error(e, kind))?;
decode(converted.value)
}
pub async fn patch_bridged<T>(
&self,
url: Url,
patch: &Patch<T>,
kind: ObjectKind,
) -> Result<(), OcpiError> {
if let Some(theirs) = self.foreign_version()
&& !kind.patch_crosses_unchanged(&patch.fields())
{
return Err(OcpiError::Unsupported(format!(
"this PATCH writes {:?}, and a {kind} does not carry {} the same way in OCPI \
{theirs} as in OCPI {}; a merge patch is not an object, so it cannot be \
translated. GET the object and PUT it back instead, which is the recovery the \
specification prescribes for a refused PATCH",
patch.fields(),
kind.divergent_fields().join(", "),
crate::CANONICAL_VERSION,
)));
}
self.patch(url, patch).await
}
pub fn list_bridged<T: DeserializeOwned + Send + 'static>(
&self,
query: PageQuery,
kind: ObjectKind,
) -> Result<PageStream<'a, T>, OcpiError> {
Ok(self.list(query)?.bridging(kind))
}
fn for_peer<T: Serialize>(
&self,
body: &T,
kind: ObjectKind,
) -> Result<Option<serde_json::Value>, OcpiError> {
let Some(theirs) = self.foreign_version() else { return Ok(None) };
let value = serde_json::to_value(body)
.map_err(|e| OcpiError::Decode { path: "/".to_owned(), message: e.to_string() })?;
let converted =
kind.bridge(&crate::CANONICAL_VERSION, theirs, value).map_err(|e| bridge_error(e, kind))?;
if let Some(note) = converted.lossy.to_status_message() {
tracing::warn!(
ocpi.peer_version = %theirs,
ocpi.object = %kind,
"{note}",
);
}
Ok(Some(converted.value))
}
async fn expect_success(&self, request: OcpiRequest) -> Result<(), OcpiError> {
let (response, _) = self
.transport
.send_with_headers::<serde_json::Value>(&request, self.peer.token(), self.peer.quirks())
.await?;
if response.is_success() { Ok(()) } else { Err(response.into_result().unwrap_err()) }
}
pub fn list<T: DeserializeOwned + Send + 'static>(
&self,
query: PageQuery,
) -> Result<PageStream<'a, T>, OcpiError> {
let endpoint = self.sender_endpoint().ok_or_else(|| self.missing(InterfaceRole::Sender))?;
let query = match self.peer.quirks().peer_max_page_limit {
Some(max) => query.clamped_to(max),
None => query,
};
Ok(PageStream::new(
self.transport,
self.peer,
self.module.clone(),
self.routing(),
endpoint.list(&query),
))
}
}
impl<'a> ModuleClient<'a> {
#[must_use]
pub fn list_from<T: DeserializeOwned + Send + 'static>(
&self,
base: &Url,
query: &PageQuery,
) -> PageStream<'a, T> {
PageStream::new(self.transport, self.peer, self.module.clone(), self.routing(), query.apply_to(base))
}
}
#[derive(Clone, Debug)]
pub struct LocationsSender<'a>(ModuleClient<'a>);
impl<'a> LocationsSender<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub fn list(
&self,
query: PageQuery,
) -> Result<PageStream<'a, crate::v2_3_0::locations::Location>, OcpiError> {
self.0.list_bridged(query, ObjectKind::Location)
}
pub async fn location(&self, location_id: &str) -> Result<crate::v2_3_0::locations::Location, OcpiError> {
let endpoint = self.0.sender_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Sender))?;
self.0.get_bridged(endpoint.location(location_id, None, None), ObjectKind::Location).await
}
pub async fn evse(
&self,
location_id: &str,
evse_uid: &str,
) -> Result<crate::v2_3_0::locations::Evse, OcpiError> {
let endpoint = self.0.sender_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Sender))?;
self.0.get_bridged(endpoint.location(location_id, Some(evse_uid), None), ObjectKind::Evse).await
}
pub async fn connector(
&self,
location_id: &str,
evse_uid: &str,
connector_id: &str,
) -> Result<crate::v2_3_0::locations::Connector, OcpiError> {
let endpoint = self.0.sender_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Sender))?;
self.0
.get_bridged(
endpoint.location(location_id, Some(evse_uid), Some(connector_id)),
ObjectKind::Connector,
)
.await
}
}
#[derive(Clone, Debug)]
pub struct LocationsReceiver<'a>(ModuleClient<'a>);
impl<'a> LocationsReceiver<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub async fn put_location(
&self,
owner: &PartyRef,
location: &crate::v2_3_0::locations::Location,
) -> Result<(), OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
self.0
.put_bridged(
endpoint.location(owner, location.id.as_str(), None, None),
location,
ObjectKind::Location,
)
.await
}
pub async fn put_evse(
&self,
owner: &PartyRef,
location_id: &str,
evse: &crate::v2_3_0::locations::Evse,
) -> Result<(), OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
self.0
.put_bridged(
endpoint.location(owner, location_id, Some(evse.uid.as_str()), None),
evse,
ObjectKind::Evse,
)
.await
}
pub async fn patch<T>(
&self,
owner: &PartyRef,
location_id: &str,
evse_uid: Option<&str>,
connector_id: Option<&str>,
patch: &Patch<T>,
) -> Result<(), OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
let kind = match (evse_uid, connector_id) {
(None, _) => ObjectKind::Location,
(Some(_), None) => ObjectKind::Evse,
(Some(_), Some(_)) => ObjectKind::Connector,
};
self.0.patch_bridged(endpoint.location(owner, location_id, evse_uid, connector_id), patch, kind).await
}
}
#[derive(Clone, Debug)]
pub struct TokensSender<'a>(ModuleClient<'a>);
impl<'a> TokensSender<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub fn list(&self, query: PageQuery) -> Result<PageStream<'a, crate::v2_3_0::tokens::Token>, OcpiError> {
self.0.list_bridged(query, ObjectKind::Token)
}
pub async fn authorize(
&self,
token_uid: &str,
token_type: Option<crate::v2_3_0::tokens::TokenType>,
location: Option<&crate::v2_3_0::tokens::LocationReferences>,
) -> Result<crate::v2_3_0::tokens::AuthorizationInfo, OcpiError> {
let endpoint = self.0.sender_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Sender))?;
let url = endpoint.token_authorize(
token_uid,
token_type.as_ref().map(super::super::v2_3_0::tokens::TokenType::as_str),
);
match location {
Some(references) => {
self.0.post_bridged(url, references, None, Some(ObjectKind::AuthorizationInfo)).await
}
None => {
self.0
.post_bridged(url, &serde_json::json!({}), None, Some(ObjectKind::AuthorizationInfo))
.await
}
}
}
}
#[derive(Clone, Debug)]
pub struct CdrsClient<'a>(ModuleClient<'a>);
impl<'a> CdrsClient<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub fn list(&self, query: PageQuery) -> Result<PageStream<'a, crate::v2_3_0::cdrs::Cdr>, OcpiError> {
self.0.list_bridged(query, ObjectKind::Cdr)
}
pub async fn post(&self, cdr: &crate::v2_3_0::cdrs::Cdr) -> Result<Option<Url>, OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
check_outgoing(cdr, self.0.transport.config())?;
let request = match self.0.for_peer(cdr, ObjectKind::Cdr)? {
Some(value) => self.0.request(Method::POST, endpoint.base().clone()).with_body(&value)?,
None => self.0.request(Method::POST, endpoint.base().clone()).with_body(cdr)?,
};
let (response, headers) = self
.0
.transport
.send_with_headers::<serde_json::Value>(&request, self.0.peer.token(), self.0.peer.quirks())
.await?;
if !response.is_success() {
return Err(response.into_result().unwrap_err());
}
Ok(crate::transport::header_str(&headers, &crate::transport::headers::LOCATION).map(Url::new_lenient))
}
}
#[derive(Clone, Debug)]
pub struct CommandsClient<'a>(ModuleClient<'a>);
impl<'a> CommandsClient<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub async fn send(
&self,
command: &crate::v2_3_0::commands::Command,
) -> Result<crate::v2_3_0::commands::CommandResponse, OcpiError> {
use crate::v2_3_0::commands::Command;
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
let url = endpoint.base().join(command.command_type().as_str());
match command {
Command::CancelReservation(c) => self.0.post_bridged(url, c, None, None).await,
Command::ReserveNow(c) => {
self.0.post_bridged(url, c.as_ref(), Some(ObjectKind::ReserveNow), None).await
}
Command::StartSession(c) => {
self.0.post_bridged(url, c.as_ref(), Some(ObjectKind::StartSession), None).await
}
Command::StopSession(c) => self.0.post_bridged(url, c, None, None).await,
Command::UnlockConnector(c) => self.0.post_bridged(url, c, None, None).await,
}
}
}
#[derive(Clone, Debug)]
pub struct SessionsSender<'a>(ModuleClient<'a>);
impl<'a> SessionsSender<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub fn list(
&self,
query: PageQuery,
) -> Result<PageStream<'a, crate::v2_3_0::sessions::Session>, OcpiError> {
self.0.list_bridged(query, ObjectKind::Session)
}
pub async fn set_charging_preferences(
&self,
session_id: &str,
preferences: &crate::v2_3_0::sessions::ChargingPreferences,
) -> Result<crate::v2_3_0::sessions::ChargingPreferencesResponse, OcpiError> {
let endpoint = self.0.sender_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Sender))?;
check_outgoing(preferences, self.0.transport.config())?;
let request =
self.0.request(Method::PUT, endpoint.charging_preferences(session_id)).with_body(preferences)?;
self.0.transport.send(&request, self.0.peer.token(), self.0.peer.quirks()).await
}
}
#[derive(Clone, Debug)]
pub struct SessionsReceiver<'a>(ModuleClient<'a>);
impl<'a> SessionsReceiver<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub async fn session(
&self,
owner: &PartyRef,
session_id: &str,
) -> Result<crate::v2_3_0::sessions::Session, OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
self.0.get_bridged(endpoint.object(owner, session_id), ObjectKind::Session).await
}
pub async fn put_session(
&self,
owner: &PartyRef,
session: &crate::v2_3_0::sessions::Session,
) -> Result<(), OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
self.0.put_bridged(endpoint.object(owner, session.id.as_str()), session, ObjectKind::Session).await
}
pub async fn patch<T>(
&self,
owner: &PartyRef,
session_id: &str,
patch: &Patch<T>,
) -> Result<(), OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
self.0.patch_bridged(endpoint.object(owner, session_id), patch, ObjectKind::Session).await
}
}
#[derive(Clone, Debug)]
pub struct TariffsSender<'a>(ModuleClient<'a>);
impl<'a> TariffsSender<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub fn list(
&self,
query: PageQuery,
) -> Result<PageStream<'a, crate::v2_3_0::tariffs::Tariff>, OcpiError> {
self.0.list_bridged(query, ObjectKind::Tariff)
}
}
#[derive(Clone, Debug)]
pub struct TariffsReceiver<'a>(ModuleClient<'a>);
impl<'a> TariffsReceiver<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub async fn tariff(
&self,
owner: &PartyRef,
tariff_id: &str,
) -> Result<crate::v2_3_0::tariffs::Tariff, OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
self.0.get_bridged(endpoint.object(owner, tariff_id), ObjectKind::Tariff).await
}
pub async fn put_tariff(
&self,
owner: &PartyRef,
tariff: &crate::v2_3_0::tariffs::Tariff,
) -> Result<(), OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
self.0.put_bridged(endpoint.object(owner, tariff.id.as_str()), tariff, ObjectKind::Tariff).await
}
pub async fn delete_tariff(&self, owner: &PartyRef, tariff_id: &str) -> Result<(), OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
self.0.delete(endpoint.object(owner, tariff_id)).await
}
}
#[derive(Clone, Debug)]
pub struct TokensReceiver<'a>(ModuleClient<'a>);
impl<'a> TokensReceiver<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub async fn token(
&self,
owner: &PartyRef,
token_uid: &str,
token_type: Option<crate::v2_3_0::tokens::TokenType>,
) -> Result<crate::v2_3_0::tokens::Token, OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
self.0
.get_bridged(
endpoint.token(owner, token_uid, token_type.as_ref().map(TokenType::as_str)),
ObjectKind::Token,
)
.await
}
pub async fn put_token(
&self,
owner: &PartyRef,
token: &crate::v2_3_0::tokens::Token,
) -> Result<(), OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
let url = endpoint.token(owner, token.uid.as_str(), Some(token.token_type.as_str()));
self.0.put_bridged(url, token, ObjectKind::Token).await
}
pub async fn patch<T>(
&self,
owner: &PartyRef,
token_uid: &str,
token_type: Option<crate::v2_3_0::tokens::TokenType>,
patch: &Patch<T>,
) -> Result<(), OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
let url = endpoint.token(owner, token_uid, token_type.as_ref().map(TokenType::as_str));
self.0.patch_bridged(url, patch, ObjectKind::Token).await
}
}
#[derive(Clone, Debug)]
pub struct ChargingProfilesClient<'a>(ModuleClient<'a>);
impl<'a> ChargingProfilesClient<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub async fn active_charging_profile(
&self,
session_id: &str,
duration_seconds: u64,
response_url: &Url,
) -> Result<crate::v2_3_0::charging_profiles::ChargingProfileResponse, OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
self.0.get(endpoint.active_charging_profile(session_id, duration_seconds, response_url)).await
}
pub async fn set_charging_profile(
&self,
session_id: &str,
request: &crate::v2_3_0::charging_profiles::SetChargingProfile,
) -> Result<crate::v2_3_0::charging_profiles::ChargingProfileResponse, OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
check_outgoing(request, self.0.transport.config())?;
let outgoing =
self.0.request(Method::PUT, endpoint.charging_profile(session_id)).with_body(request)?;
self.0.transport.send(&outgoing, self.0.peer.token(), self.0.peer.quirks()).await
}
pub async fn clear_charging_profile(
&self,
session_id: &str,
response_url: &Url,
) -> Result<crate::v2_3_0::charging_profiles::ChargingProfileResponse, OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
let outgoing =
self.0.request(Method::DELETE, endpoint.clear_charging_profile(session_id, response_url));
self.0.transport.send(&outgoing, self.0.peer.token(), self.0.peer.quirks()).await
}
pub async fn push_active_charging_profile(
&self,
session_id: &str,
profile: &crate::v2_3_0::charging_profiles::ActiveChargingProfile,
) -> Result<(), OcpiError> {
let endpoint = self.0.sender_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Sender))?;
self.0.put(endpoint.object(session_id), profile).await
}
}
#[derive(Clone, Debug)]
pub struct HubClientInfoClient<'a>(ModuleClient<'a>);
impl<'a> HubClientInfoClient<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
pub fn list(
&self,
query: PageQuery,
) -> Result<PageStream<'a, crate::v2_3_0::hub_client_info::ClientInfo>, OcpiError> {
self.0.list_bridged(query, ObjectKind::ClientInfo)
}
pub async fn client_info(
&self,
party: &PartyRef,
) -> Result<crate::v2_3_0::hub_client_info::ClientInfo, OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
let url = endpoint.base().join(party.country_code.as_str()).join(party.party_id.as_str());
self.0.get_bridged(url, ObjectKind::ClientInfo).await
}
pub async fn put_client_info(
&self,
info: &crate::v2_3_0::hub_client_info::ClientInfo,
) -> Result<(), OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
let url = endpoint.base().join(info.country_code.as_str()).join(info.party_id.as_str());
self.0.put_bridged(url, info, ObjectKind::ClientInfo).await
}
}
#[derive(Clone, Debug)]
pub struct PaymentsClient<'a>(ModuleClient<'a>);
impl<'a> PaymentsClient<'a> {
#[must_use]
pub const fn new(client: ModuleClient<'a>) -> Self {
Self(client)
}
fn terminals(&self) -> Result<SenderEndpoint, OcpiError> {
Ok(self
.0
.sender_endpoint()
.ok_or_else(|| self.0.missing(InterfaceRole::Sender))?
.payments_terminals())
}
fn confirmations(&self) -> Result<SenderEndpoint, OcpiError> {
Ok(self
.0
.sender_endpoint()
.ok_or_else(|| self.0.missing(InterfaceRole::Sender))?
.payments_financial_advice_confirmations())
}
pub fn list_terminals(
&self,
query: PageQuery,
) -> Result<PageStream<'a, crate::v2_3_0::payments::Terminal>, OcpiError> {
let endpoint = self.terminals()?;
Ok(PageStream::new(
self.0.transport,
self.0.peer,
self.0.module.clone(),
self.0.routing(),
endpoint.list(&query),
))
}
pub async fn terminal(&self, terminal_id: &str) -> Result<crate::v2_3_0::payments::Terminal, OcpiError> {
self.0.get(self.terminals()?.terminal(terminal_id)).await
}
pub async fn put_terminal(
&self,
terminal: &crate::v2_3_0::payments::Terminal,
) -> Result<crate::v2_3_0::payments::Terminal, OcpiError> {
check_outgoing(terminal, self.0.transport.config())?;
let url = self.terminals()?.terminal(terminal.terminal_id.as_str());
let request = self.0.request(Method::PUT, url).with_body(terminal)?;
self.0.transport.send(&request, self.0.peer.token(), self.0.peer.quirks()).await
}
pub async fn patch_terminal<T>(
&self,
terminal_id: &str,
patch: &Patch<T>,
) -> Result<crate::v2_3_0::payments::Terminal, OcpiError> {
let url = self.terminals()?.terminal(terminal_id);
let request = self.0.request(Method::PATCH, url).with_body(patch.as_value())?;
self.0.transport.send(&request, self.0.peer.token(), self.0.peer.quirks()).await
}
pub async fn activate_terminal<T>(
&self,
terminal: &Patch<T>,
) -> Result<crate::v2_3_0::payments::Terminal, OcpiError> {
let request = self
.0
.request(Method::POST, self.terminals()?.terminal_activate())
.with_body(terminal.as_value())?;
self.0.transport.send(&request, self.0.peer.token(), self.0.peer.quirks()).await
}
pub async fn deactivate_terminal(
&self,
terminal_id: &str,
) -> Result<crate::v2_3_0::payments::Terminal, OcpiError> {
let url = self.terminals()?.terminal_deactivate(terminal_id);
let request = self.0.request(Method::POST, url).with_body(&serde_json::json!({}))?;
self.0.transport.send(&request, self.0.peer.token(), self.0.peer.quirks()).await
}
pub fn list_financial_advice_confirmations(
&self,
query: PageQuery,
) -> Result<PageStream<'a, crate::v2_3_0::payments::FinancialAdviceConfirmation>, OcpiError> {
let endpoint = self.confirmations()?;
Ok(PageStream::new(
self.0.transport,
self.0.peer,
self.0.module.clone(),
self.0.routing(),
endpoint.list(&query),
))
}
pub async fn financial_advice_confirmation(
&self,
id: &str,
) -> Result<crate::v2_3_0::payments::FinancialAdviceConfirmation, OcpiError> {
self.0.get(self.confirmations()?.object(id)).await
}
pub async fn post_terminal_to_receiver(
&self,
terminal: &crate::v2_3_0::payments::Terminal,
) -> Result<crate::v2_3_0::payments::Terminal, OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
check_outgoing(terminal, self.0.transport.config())?;
let url = endpoint.payments_terminals().base().clone();
let request = self.0.request(Method::POST, url).with_body(terminal)?;
self.0.transport.send(&request, self.0.peer.token(), self.0.peer.quirks()).await
}
pub async fn post_financial_advice_confirmation(
&self,
confirmation: &crate::v2_3_0::payments::FinancialAdviceConfirmation,
) -> Result<crate::v2_3_0::payments::FinancialAdviceConfirmation, OcpiError> {
let endpoint = self.0.receiver_endpoint().ok_or_else(|| self.0.missing(InterfaceRole::Receiver))?;
check_outgoing(confirmation, self.0.transport.config())?;
let url = endpoint.payments_financial_advice_confirmations().base().clone();
let request = self.0.request(Method::POST, url).with_body(confirmation)?;
self.0.transport.send(&request, self.0.peer.token(), self.0.peer.quirks()).await
}
}
impl Peer {
#[must_use]
pub fn module<'a>(
&'a self,
transport: &'a Transport,
module: ModuleId,
from: PartyRef,
) -> ModuleClient<'a> {
ModuleClient::new(transport, self, module, from)
}
#[must_use]
pub fn locations<'a>(&'a self, transport: &'a Transport, from: PartyRef) -> LocationsSender<'a> {
LocationsSender::new(self.module(transport, ModuleId::Locations, from))
}
#[must_use]
pub fn locations_receiver<'a>(
&'a self,
transport: &'a Transport,
from: PartyRef,
) -> LocationsReceiver<'a> {
LocationsReceiver::new(self.module(transport, ModuleId::Locations, from))
}
#[must_use]
pub fn tokens<'a>(&'a self, transport: &'a Transport, from: PartyRef) -> TokensSender<'a> {
TokensSender::new(self.module(transport, ModuleId::Tokens, from))
}
#[must_use]
pub fn cdrs<'a>(&'a self, transport: &'a Transport, from: PartyRef) -> CdrsClient<'a> {
CdrsClient::new(self.module(transport, ModuleId::Cdrs, from))
}
#[must_use]
pub fn tokens_receiver<'a>(&'a self, transport: &'a Transport, from: PartyRef) -> TokensReceiver<'a> {
TokensReceiver::new(self.module(transport, ModuleId::Tokens, from))
}
#[must_use]
pub fn sessions<'a>(&'a self, transport: &'a Transport, from: PartyRef) -> SessionsSender<'a> {
SessionsSender::new(self.module(transport, ModuleId::Sessions, from))
}
#[must_use]
pub fn sessions_receiver<'a>(&'a self, transport: &'a Transport, from: PartyRef) -> SessionsReceiver<'a> {
SessionsReceiver::new(self.module(transport, ModuleId::Sessions, from))
}
#[must_use]
pub fn tariffs<'a>(&'a self, transport: &'a Transport, from: PartyRef) -> TariffsSender<'a> {
TariffsSender::new(self.module(transport, ModuleId::Tariffs, from))
}
#[must_use]
pub fn tariffs_receiver<'a>(&'a self, transport: &'a Transport, from: PartyRef) -> TariffsReceiver<'a> {
TariffsReceiver::new(self.module(transport, ModuleId::Tariffs, from))
}
#[must_use]
pub fn commands<'a>(&'a self, transport: &'a Transport, from: PartyRef) -> CommandsClient<'a> {
CommandsClient::new(self.module(transport, ModuleId::Commands, from))
}
#[must_use]
pub fn charging_profiles<'a>(
&'a self,
transport: &'a Transport,
from: PartyRef,
) -> ChargingProfilesClient<'a> {
ChargingProfilesClient::new(self.module(transport, ModuleId::ChargingProfiles, from))
}
#[must_use]
pub fn hub_client_info<'a>(
&'a self,
transport: &'a Transport,
from: PartyRef,
) -> HubClientInfoClient<'a> {
HubClientInfoClient::new(self.module(transport, ModuleId::HubClientInfo, from))
}
#[must_use]
pub fn payments<'a>(&'a self, transport: &'a Transport, from: PartyRef) -> PaymentsClient<'a> {
PaymentsClient::new(self.module(transport, ModuleId::Payments, from))
}
}
fn bridge_error(error: BridgeError, kind: ObjectKind) -> OcpiError {
match error {
BridgeError::Unsupported { from, to } => OcpiError::Unsupported(format!(
"this build has no conversions between OCPI {from} and OCPI {to}, so a {kind} cannot \
be carried between them"
)),
BridgeError::Decode { version, message, .. } => OcpiError::Decode {
path: "/".to_owned(),
message: format!("the peer's OCPI {version} {kind} could not be read: {message}"),
},
}
}
fn decode<T: DeserializeOwned>(value: serde_json::Value) -> Result<T, OcpiError> {
serde_path_to_error::deserialize(value)
.map_err(|e| OcpiError::Decode { path: e.path().to_string(), message: e.into_inner().to_string() })
}
#[must_use]
pub fn correlated_ids() -> RequestIds {
RequestIds::generate()
}