use core::fmt;
use core::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum Protocol {
Din70121,
Iso2,
Iso20,
}
impl Protocol {
pub const ALL: &'static [Self] = &[Self::Iso20, Self::Iso2, Self::Din70121];
pub const COUNT: usize = Self::ALL.len();
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Din70121 => "din70121",
Self::Iso2 => "iso15118-2",
Self::Iso20 => "iso15118-20",
}
}
#[must_use]
pub const fn title(self) -> &'static str {
match self {
Self::Din70121 => "DIN SPEC 70121:2014",
Self::Iso2 => "ISO 15118-2:2014",
Self::Iso20 => "ISO 15118-20:2022",
}
}
#[must_use]
pub const fn namespace(self) -> &'static str {
match self {
Self::Din70121 => "urn:din:70121:2012:MsgDef",
Self::Iso2 => "urn:iso:15118:2:2013:MsgDef",
Self::Iso20 => "urn:iso:std:iso:15118:-20:CommonMessages",
}
}
#[must_use]
#[allow(clippy::match_same_arms, reason = "one arm per protocol reads as a table")]
pub const fn version(self) -> (u32, u32) {
match self {
Self::Din70121 => (2, 0),
Self::Iso2 => (2, 0),
Self::Iso20 => (1, 0),
}
}
#[must_use]
pub fn from_namespace(ns: &str) -> Option<Self> {
Self::ALL.iter().copied().find(|p| p.namespace() == ns)
}
#[must_use]
pub const fn requires_tls(self) -> bool {
matches!(self, Self::Iso20)
}
}
impl fmt::Display for Protocol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for Protocol {
type Err = ParseProtocolError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if let Some(p) = Self::ALL.iter().copied().find(|p| p.as_str().eq_ignore_ascii_case(s)) {
return Ok(p);
}
if let Some(p) = Self::from_namespace(s) {
return Ok(p);
}
Err(ParseProtocolError::for_input(s))
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Protocol {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Protocol {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct V;
impl serde::de::Visitor<'_> for V {
type Value = Protocol;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(EXPECTED)
}
fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Protocol, E> {
s.parse().map_err(serde::de::Error::custom)
}
}
deserializer.deserialize_str(V)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ParseProtocolError {
generation_omitted: bool,
}
const EXPECTED: &str = r#"one of "din70121", "iso15118-2", "iso15118-20""#;
impl ParseProtocolError {
fn for_input(input: &str) -> Self {
let generation_omitted = input.eq_ignore_ascii_case("iso15118")
|| input.eq_ignore_ascii_case("iso 15118")
|| input.eq_ignore_ascii_case("iso-15118");
Self { generation_omitted }
}
#[must_use]
pub const fn generation_omitted(self) -> bool {
self.generation_omitted
}
}
impl fmt::Display for ParseProtocolError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.generation_omitted {
return write!(f, "\"iso15118\" does not say which generation — expected {EXPECTED}");
}
write!(f, "unrecognised protocol name — expected {EXPECTED}")
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseProtocolError {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Protocols {
entries: [Option<Protocol>; Protocol::COUNT],
}
impl Protocols {
#[must_use]
pub const fn new() -> Self {
Self { entries: [None; Protocol::COUNT] }
}
pub const ALL: Self = Self::from_slice(Protocol::ALL);
pub const ISO: Self = Self::new().with(Protocol::Iso20).with(Protocol::Iso2);
#[must_use]
pub const fn only(protocol: Protocol) -> Self {
Self::new().with(protocol)
}
#[must_use]
pub const fn with(mut self, protocol: Protocol) -> Self {
if self.contains(protocol) {
return self;
}
let mut i = 0;
while i < Protocol::COUNT {
if self.entries[i].is_none() {
self.entries[i] = Some(protocol);
return self;
}
i += 1;
}
self
}
#[must_use]
pub const fn from_slice(protocols: &[Protocol]) -> Self {
let mut set = Self::new();
let mut i = 0;
while i < protocols.len() {
set = set.with(protocols[i]);
i += 1;
}
set
}
pub const fn insert(&mut self, protocol: Protocol) -> bool {
if self.contains(protocol) {
return false;
}
*self = self.with(protocol);
true
}
pub fn remove(&mut self, protocol: Protocol) -> bool {
let before = self.len();
self.retain(|p| p != protocol);
self.len() != before
}
#[must_use]
pub const fn contains(&self, protocol: Protocol) -> bool {
let mut i = 0;
while i < Protocol::COUNT {
if let Some(p) = self.entries[i]
&& p as u8 == protocol as u8
{
return true;
}
i += 1;
}
false
}
pub fn retain(&mut self, mut f: impl FnMut(Protocol) -> bool) {
let kept = *self;
*self = Self::new();
for p in kept {
if f(p) {
*self = self.with(p);
}
}
}
#[must_use]
pub const fn len(&self) -> usize {
let mut n = 0;
let mut i = 0;
while i < Protocol::COUNT {
if self.entries[i].is_some() {
n += 1;
}
i += 1;
}
n
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.entries[0].is_none()
}
#[must_use]
pub const fn first(&self) -> Option<Protocol> {
self.entries[0]
}
#[must_use]
pub fn best(&self) -> Option<Protocol> {
self.iter().max()
}
#[must_use]
pub const fn iter(&self) -> Iter {
Iter { entries: self.entries, next: 0 }
}
}
impl IntoIterator for Protocols {
type Item = Protocol;
type IntoIter = Iter;
fn into_iter(self) -> Iter {
self.iter()
}
}
impl IntoIterator for &Protocols {
type Item = Protocol;
type IntoIter = Iter;
fn into_iter(self) -> Iter {
self.iter()
}
}
impl FromIterator<Protocol> for Protocols {
fn from_iter<I: IntoIterator<Item = Protocol>>(iter: I) -> Self {
let mut set = Self::new();
for p in iter {
set.insert(p);
}
set
}
}
impl From<Protocol> for Protocols {
fn from(protocol: Protocol) -> Self {
Self::only(protocol)
}
}
impl From<&[Protocol]> for Protocols {
fn from(protocols: &[Protocol]) -> Self {
Self::from_slice(protocols)
}
}
impl<const N: usize> From<[Protocol; N]> for Protocols {
fn from(protocols: [Protocol; N]) -> Self {
Self::from_slice(&protocols)
}
}
impl<const N: usize> From<&[Protocol; N]> for Protocols {
fn from(protocols: &[Protocol; N]) -> Self {
Self::from_slice(protocols)
}
}
#[derive(Debug, Clone)]
pub struct Iter {
entries: [Option<Protocol>; Protocol::COUNT],
next: usize,
}
impl Iterator for Iter {
type Item = Protocol;
fn next(&mut self) -> Option<Protocol> {
while self.next < Protocol::COUNT {
let entry = self.entries[self.next];
self.next += 1;
if entry.is_some() {
return entry;
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.entries[self.next..].iter().flatten().count();
(n, Some(n))
}
}
impl ExactSizeIterator for Iter {}
impl core::iter::FusedIterator for Iter {}
impl fmt::Debug for Protocols {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter().map(Protocol::as_str)).finish()
}
}
impl fmt::Display for Protocols {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, p) in self.iter().enumerate() {
if i > 0 {
f.write_str(",")?;
}
f.write_str(p.as_str())?;
}
Ok(())
}
}
impl FromStr for Protocols {
type Err = ParseProtocolError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut set = Self::new();
for token in s.split(',') {
if token.trim().is_empty() {
continue;
}
set.insert(token.parse()?);
}
Ok(set)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Protocols {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_seq(self.iter().map(Protocol::as_str))
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Protocols {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = Protocols;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a sequence of protocol names, or one comma-separated string")
}
fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Protocols, E> {
s.parse().map_err(serde::de::Error::custom)
}
fn visit_seq<A: serde::de::SeqAccess<'de>>(
self,
mut seq: A,
) -> Result<Protocols, A::Error> {
let mut set = Protocols::new();
while let Some(p) = seq.next_element::<Protocol>()? {
set.insert(p);
}
Ok(set)
}
}
deserializer.deserialize_any(V)
}
}
#[cfg(test)]
mod tests {
use alloc::format;
use alloc::string::ToString;
use super::*;
#[test]
fn namespaces_round_trip() {
for &p in Protocol::ALL {
assert_eq!(Protocol::from_namespace(p.namespace()), Some(p));
}
assert_eq!(Protocol::from_namespace("urn:nope"), None);
}
#[test]
fn only_dash_20_mandates_tls() {
assert!(Protocol::Iso20.requires_tls());
assert!(!Protocol::Iso2.requires_tls());
assert!(!Protocol::Din70121.requires_tls());
}
#[test]
fn newer_protocols_sort_higher() {
assert!(Protocol::Iso20 > Protocol::Iso2);
assert!(Protocol::Iso2 > Protocol::Din70121);
}
#[test]
fn short_names_round_trip() {
for &p in Protocol::ALL {
assert_eq!(p.as_str().parse(), Ok(p));
assert_eq!(p.to_string().parse(), Ok(p));
assert_eq!(p.namespace().parse(), Ok(p));
}
}
#[test]
fn short_names_say_which_generation() {
assert_eq!(Protocol::Din70121.as_str(), "din70121");
assert_eq!(Protocol::Iso2.as_str(), "iso15118-2");
assert_eq!(Protocol::Iso20.as_str(), "iso15118-20");
assert!(Protocol::Iso20.as_str().starts_with(Protocol::Iso2.as_str()));
assert_eq!("iso15118-2".parse(), Ok(Protocol::Iso2));
assert_eq!("iso15118-20".parse(), Ok(Protocol::Iso20));
assert!("iso15118-200".parse::<Protocol>().is_err());
}
#[test]
fn a_name_without_a_generation_is_refused_by_name() {
for input in ["iso15118", "ISO15118", "iso 15118", "ISO-15118"] {
let err = input.parse::<Protocol>().expect_err("ambiguous");
assert!(err.generation_omitted(), "{input}");
assert!(err.to_string().contains("which generation"), "{input}");
}
let err = "nonsense".parse::<Protocol>().expect_err("unknown");
assert!(!err.generation_omitted());
}
#[test]
fn parsing_ignores_ascii_case_and_surrounding_space() {
assert_eq!(" ISO15118-20 ".parse(), Ok(Protocol::Iso20));
assert_eq!("DIN70121".parse(), Ok(Protocol::Din70121));
}
#[test]
fn a_set_keeps_preference_order_and_drops_duplicates() {
let set = Protocols::from_slice(&[
Protocol::Iso2,
Protocol::Iso20,
Protocol::Iso2,
Protocol::Din70121,
]);
assert_eq!(set.len(), 3);
assert_eq!(
set.iter().collect::<alloc::vec::Vec<_>>(),
[Protocol::Iso2, Protocol::Iso20, Protocol::Din70121],
"a repeat keeps the first, more-preferred position"
);
assert_eq!(set.first(), Some(Protocol::Iso2), "preference");
assert_eq!(set.best(), Some(Protocol::Iso20), "generation");
}
#[test]
fn the_two_all_constants_cannot_disagree() {
assert_eq!(Protocols::from_slice(Protocol::ALL), Protocols::ALL);
assert_eq!(Protocols::ALL.len(), Protocol::COUNT);
for &p in Protocol::ALL {
assert!(Protocols::ALL.contains(p));
}
}
#[test]
fn equality_is_order_sensitive_because_the_order_is_a_priority() {
let prefers_new = Protocols::from_slice(&[Protocol::Iso20, Protocol::Iso2]);
let prefers_old = Protocols::from_slice(&[Protocol::Iso2, Protocol::Iso20]);
assert_ne!(prefers_new, prefers_old, "these two vehicles are asking for different things");
assert!(Protocol::ALL.iter().all(|&p| prefers_new.contains(p) == prefers_old.contains(p)));
assert_eq!(prefers_new.best(), prefers_old.best());
}
#[test]
fn a_set_cannot_overflow_however_long_the_slice() {
let long = [Protocol::Iso2; 64];
assert_eq!(Protocols::from_slice(&long).len(), 1);
}
#[test]
fn set_membership_and_removal() {
let mut set = Protocols::ISO;
assert!(set.contains(Protocol::Iso20));
assert!(!set.contains(Protocol::Din70121));
assert!(set.remove(Protocol::Iso20));
assert!(!set.remove(Protocol::Iso20));
assert_eq!(set.iter().collect::<alloc::vec::Vec<_>>(), [Protocol::Iso2]);
assert!(set.insert(Protocol::Din70121));
assert!(!set.insert(Protocol::Din70121));
assert_eq!(
set.iter().collect::<alloc::vec::Vec<_>>(),
[Protocol::Iso2, Protocol::Din70121],
"an insert appends at the least-preferred end"
);
}
#[test]
fn retain_preserves_order() {
let mut set = Protocols::ALL;
set.retain(|p| p != Protocol::Iso2);
assert_eq!(
set.iter().collect::<alloc::vec::Vec<_>>(),
[Protocol::Iso20, Protocol::Din70121]
);
}
#[test]
fn the_empty_set_is_empty_in_every_direction() {
let set = Protocols::new();
assert!(set.is_empty());
assert_eq!(set.len(), 0);
assert_eq!(set.first(), None);
assert_eq!(set.best(), None);
assert_eq!(set.to_string(), "");
assert_eq!("".parse(), Ok(set));
assert_eq!(set.iter().count(), 0);
}
#[test]
fn a_set_round_trips_through_its_own_text() {
for set in
[Protocols::new(), Protocols::ISO, Protocols::ALL, Protocols::only(Protocol::Iso2)]
{
assert_eq!(set.to_string().parse(), Ok(set), "{set:?}");
}
assert_eq!(Protocols::ISO.to_string(), "iso15118-20,iso15118-2");
assert_eq!(
" iso15118-2 , din70121 ".parse(),
Ok(Protocols::from_slice(&[Protocol::Iso2, Protocol::Din70121]))
);
}
#[test]
fn one_bad_name_fails_the_whole_set() {
assert!("iso15118-20,nonsense".parse::<Protocols>().is_err());
assert!("iso15118-20,iso15118".parse::<Protocols>().is_err());
}
#[test]
fn iter_reports_its_own_length() {
let mut it = Protocols::ISO.iter();
assert_eq!(it.len(), 2);
assert_eq!(it.next(), Some(Protocol::Iso20));
assert_eq!(it.len(), 1);
assert_eq!(it.collect::<alloc::vec::Vec<_>>(), [Protocol::Iso2]);
}
#[test]
fn a_set_debugs_as_its_stable_names() {
assert_eq!(format!("{:?}", Protocols::ISO), r#"["iso15118-20", "iso15118-2"]"#);
}
#[test]
fn collecting_builds_a_set() {
let set: Protocols =
[Protocol::Iso20, Protocol::Iso2, Protocol::Iso20].into_iter().collect();
assert_eq!(set, Protocols::ISO);
assert_eq!(Protocols::from([Protocol::Iso20, Protocol::Iso2]), Protocols::ISO);
assert_eq!(Protocols::from(Protocol::Iso2), Protocols::only(Protocol::Iso2));
}
#[cfg(feature = "serde")]
#[test]
fn serde_uses_the_stable_name_not_the_variant_name() {
assert_eq!(serde_json::to_string(&Protocol::Iso20).unwrap(), r#""iso15118-20""#);
assert_eq!(serde_json::from_str::<Protocol>(r#""iso15118-20""#).unwrap(), Protocol::Iso20);
assert!(serde_json::from_str::<Protocol>(r#""Iso20""#).is_err());
}
#[cfg(feature = "serde")]
#[test]
fn a_set_deserialises_from_a_sequence_or_a_string() {
assert_eq!(
serde_json::to_string(&Protocols::ISO).unwrap(),
r#"["iso15118-20","iso15118-2"]"#
);
assert_eq!(
serde_json::from_str::<Protocols>(r#"["iso15118-20","iso15118-2"]"#).unwrap(),
Protocols::ISO
);
assert_eq!(
serde_json::from_str::<Protocols>(r#""iso15118-20,iso15118-2""#).unwrap(),
Protocols::ISO
);
}
}