use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum OcppVersion {
V16,
V201,
V21,
}
impl OcppVersion {
pub const ALL: [OcppVersion; 3] = [OcppVersion::V16, OcppVersion::V201, OcppVersion::V21];
pub const fn subprotocol(self) -> &'static str {
match self {
OcppVersion::V16 => "ocpp1.6",
OcppVersion::V201 => "ocpp2.0.1",
OcppVersion::V21 => "ocpp2.1",
}
}
pub fn from_subprotocol(s: &str) -> Option<OcppVersion> {
match s.trim().to_ascii_lowercase().as_str() {
"ocpp1.6" => Some(OcppVersion::V16),
"ocpp2.0.1" => Some(OcppVersion::V201),
"ocpp2.1" => Some(OcppVersion::V21),
_ => None,
}
}
pub const fn dialect(self) -> crate::ErrorDialect {
match self {
OcppVersion::V16 => crate::ErrorDialect::Ocpp16,
_ => crate::ErrorDialect::Ocpp2,
}
}
pub const fn as_str(self) -> &'static str {
match self {
OcppVersion::V16 => "1.6",
OcppVersion::V201 => "2.0.1",
OcppVersion::V21 => "2.1",
}
}
}
impl fmt::Display for OcppVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for OcppVersion {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim() {
"1.6" | "16" | "ocpp1.6" => Ok(OcppVersion::V16),
"2.0.1" | "201" | "ocpp2.0.1" => Ok(OcppVersion::V201),
"2.1" | "21" | "ocpp2.1" => Ok(OcppVersion::V21),
other => Err(format!("unknown OCPP version '{other}'")),
}
}
}
pub fn negotiate<'a, I>(offered: I, acceptable: &[OcppVersion]) -> Option<(OcppVersion, &'a str)>
where
I: IntoIterator<Item = &'a str>,
{
offered
.into_iter()
.flat_map(|h| h.split(','))
.map(str::trim)
.filter(|t| !t.is_empty())
.find_map(|t| {
OcppVersion::from_subprotocol(t)
.filter(|v| acceptable.contains(v))
.map(|v| (v, t))
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn negotiation_respects_client_preference() {
let all = OcppVersion::ALL;
assert_eq!(
negotiate(["ocpp2.1, ocpp2.0.1, ocpp1.6"], &all),
Some((OcppVersion::V21, "ocpp2.1"))
);
assert_eq!(
negotiate(["ocpp1.6, ocpp2.0.1"], &all),
Some((OcppVersion::V16, "ocpp1.6"))
);
assert_eq!(
negotiate(["ocpp2.1", "ocpp1.6"], &[OcppVersion::V16]),
Some((OcppVersion::V16, "ocpp1.6"))
);
assert_eq!(negotiate(["ocpp1.5, ocpp1.8"], &all), None);
assert_eq!(negotiate([], &all), None);
assert_eq!(
negotiate(["OCPP1.6"], &all),
Some((OcppVersion::V16, "OCPP1.6"))
);
}
}