1use std::fmt;
2use std::ops::Deref;
3use std::str::FromStr;
4use std::sync::Arc;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct FleetName {
11 bytes: [u8; 8],
12 len: u8,
13}
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub struct InvalidFleetName;
17
18impl fmt::Display for InvalidFleetName {
19 fn fmt(
20 &self,
21 f: &mut fmt::Formatter<'_>,
22 ) -> fmt::Result {
23 f.write_str(
24 "fleet name must contain 1–8 ASCII letters, digits, '-' or '_'",
25 )
26 }
27}
28
29impl std::error::Error for InvalidFleetName {}
30
31impl FleetName {
32 pub const MAX_LEN: usize = 8;
34
35 pub fn new(name: impl AsRef<str>) -> Self {
36 Self::try_new(name).expect("invalid fleet name")
37 }
38
39 pub fn try_new(name: impl AsRef<str>) -> Result<Self, InvalidFleetName> {
40 let name = name.as_ref();
41 if name.is_empty()
42 || name.len() > Self::MAX_LEN
43 || !name
44 .bytes()
45 .all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')
46 {
47 return Err(InvalidFleetName);
48 }
49 let mut bytes = [0; 8];
50 bytes[..name.len()].copy_from_slice(name.as_bytes());
51 Ok(Self { bytes, len: name.len() as u8 })
52 }
53
54 pub fn as_str(&self) -> &str {
55 std::str::from_utf8(&self.bytes[..usize::from(self.len)])
56 .expect("fleet name contains validated ASCII")
57 }
58}
59
60impl FromStr for FleetName {
61 type Err = InvalidFleetName;
62
63 fn from_str(name: &str) -> Result<Self, Self::Err> {
64 Self::try_new(name)
65 }
66}
67
68impl From<&str> for FleetName {
69 fn from(name: &str) -> Self {
70 Self::new(name)
71 }
72}
73
74impl From<String> for FleetName {
75 fn from(name: String) -> Self {
76 Self::new(name)
77 }
78}
79
80impl From<Arc<str>> for FleetName {
81 fn from(name: Arc<str>) -> Self {
82 Self::new(name)
83 }
84}
85
86impl From<FleetName> for Arc<str> {
87 fn from(name: FleetName) -> Self {
88 Arc::from(name.as_str())
89 }
90}
91
92impl From<FleetName> for String {
93 fn from(name: FleetName) -> Self {
94 name.as_str().to_owned()
95 }
96}
97
98impl AsRef<str> for FleetName {
99 fn as_ref(&self) -> &str {
100 self.as_str()
101 }
102}
103
104impl Deref for FleetName {
105 type Target = str;
106
107 fn deref(&self) -> &str {
108 self.as_str()
109 }
110}
111
112impl fmt::Display for FleetName {
113 fn fmt(
114 &self,
115 f: &mut fmt::Formatter<'_>,
116 ) -> fmt::Result {
117 f.write_str(self.as_str())
118 }
119}