use bon::Builder;
use serde::{Deserialize, Serialize};
use crate::ocpi_lenient_enum;
use crate::types::validate_fields;
use crate::types::{DateTime, Extensions, OcpiString, Url, Validate, Validator};
use super::tokens::Token;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct CommandResponse {
pub result: CommandResponseType,
#[serde(flatten, default, skip_serializing_if = "Extensions::is_empty")]
pub extensions: Extensions,
}
impl CommandResponse {
#[must_use]
pub fn new(result: CommandResponseType) -> Self {
Self { result, extensions: Extensions::new() }
}
}
impl Validate for CommandResponse {
fn validate_in(&self, v: &mut Validator) {
validate_fields!(self, v, result);
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Builder)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[builder(on(_, into))]
pub struct StartSession {
pub response_url: Url,
pub token: Token,
pub location_id: OcpiString<39>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub evse_uid: Option<OcpiString<39>>,
#[serde(flatten, default, skip_serializing_if = "Extensions::is_empty")]
#[builder(default)]
pub extensions: Extensions,
}
impl Validate for StartSession {
fn validate_in(&self, v: &mut Validator) {
validate_fields!(self, v, response_url, token, location_id, evse_uid);
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Builder)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[builder(on(_, into))]
pub struct StopSession {
pub response_url: Url,
pub session_id: OcpiString<36>,
#[serde(flatten, default, skip_serializing_if = "Extensions::is_empty")]
#[builder(default)]
pub extensions: Extensions,
}
impl Validate for StopSession {
fn validate_in(&self, v: &mut Validator) {
validate_fields!(self, v, response_url, session_id);
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Builder)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[builder(on(_, into))]
pub struct ReserveNow {
pub response_url: Url,
pub token: Token,
pub expiry_date: DateTime,
pub reservation_id: i64,
pub location_id: OcpiString<39>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub evse_uid: Option<OcpiString<39>>,
#[serde(flatten, default, skip_serializing_if = "Extensions::is_empty")]
#[builder(default)]
pub extensions: Extensions,
}
impl Validate for ReserveNow {
fn validate_in(&self, v: &mut Validator) {
validate_fields!(self, v, response_url, token, expiry_date, location_id, evse_uid);
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Builder)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[builder(on(_, into))]
pub struct UnlockConnector {
pub response_url: Url,
pub location_id: OcpiString<39>,
pub evse_uid: OcpiString<39>,
pub connector_id: OcpiString<36>,
#[serde(flatten, default, skip_serializing_if = "Extensions::is_empty")]
#[builder(default)]
pub extensions: Extensions,
}
impl Validate for UnlockConnector {
fn validate_in(&self, v: &mut Validator) {
validate_fields!(self, v, response_url, location_id, evse_uid, connector_id);
}
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum Command {
ReserveNow(Box<ReserveNow>),
StartSession(Box<StartSession>),
StopSession(StopSession),
UnlockConnector(UnlockConnector),
}
impl Command {
#[must_use]
pub fn command_type(&self) -> CommandType {
match self {
Self::ReserveNow(_) => CommandType::ReserveNow,
Self::StartSession(_) => CommandType::StartSession,
Self::StopSession(_) => CommandType::StopSession,
Self::UnlockConnector(_) => CommandType::UnlockConnector,
}
}
#[must_use]
pub fn response_url(&self) -> &Url {
match self {
Self::ReserveNow(c) => &c.response_url,
Self::StartSession(c) => &c.response_url,
Self::StopSession(c) => &c.response_url,
Self::UnlockConnector(c) => &c.response_url,
}
}
}
impl Validate for Command {
fn validate_in(&self, v: &mut Validator) {
match self {
Self::ReserveNow(c) => c.validate_in(v),
Self::StartSession(c) => c.validate_in(v),
Self::StopSession(c) => c.validate_in(v),
Self::UnlockConnector(c) => c.validate_in(v),
}
}
}
ocpi_lenient_enum! {
pub enum CommandResponseType {
NotSupported = "NOT_SUPPORTED",
Rejected = "REJECTED",
Accepted = "ACCEPTED",
Timeout = "TIMEOUT",
UnknownSession = "UNKNOWN_SESSION",
}
}
ocpi_lenient_enum! {
pub enum CommandType {
ReserveNow = "RESERVE_NOW",
StartSession = "START_SESSION",
StopSession = "STOP_SESSION",
UnlockConnector = "UNLOCK_CONNECTOR",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn timeout_is_a_response_type_here_because_there_is_no_result_object() {
let response: CommandResponse = serde_json::from_str(r#"{"result":"TIMEOUT"}"#).unwrap();
assert_eq!(response.result, CommandResponseType::Timeout);
assert!("TIMEOUT".parse::<crate::v2_3_0::commands::CommandResponseType>().is_err());
assert!("TIMEOUT".parse::<crate::v2_3_0::commands::CommandResultType>().is_ok());
}
#[test]
fn cancel_reservation_arrived_after_2_1_1() {
assert_eq!(CommandType::ALL_KNOWN.len(), 4);
assert!(!CommandType::from("CANCEL_RESERVATION").is_known());
}
#[test]
fn the_reservation_id_is_an_integer() {
#[derive(serde::Deserialize)]
struct OnlyId {
reservation_id: i64,
}
let json = r#"{"reservation_id":42}"#;
let parsed: OnlyId = serde_json::from_str(json).unwrap();
assert_eq!(parsed.reservation_id, 42);
}
}