iceoryx2_bb_system_types/
file_name.rs1pub use iceoryx2_bb_container::semantic_string::SemanticString;
33
34use core::hash::{Hash, Hasher};
35
36use alloc::string::String;
37
38use iceoryx2_bb_container::semantic_string;
39use iceoryx2_bb_derive_macros::ZeroCopySend;
40use iceoryx2_bb_elementary::static_assert::{static_assert_ge, static_assert_le};
41use iceoryx2_bb_elementary_traits::zero_copy_send::ZeroCopySend;
42use iceoryx2_pal_configuration::FILENAME_LENGTH;
43
44fn invalid_characters(value: &[u8]) -> bool {
45 for c in value {
46 match c {
47 0 => return true,
49 b'/' => return true,
50 1..=31 => return true,
52 #[cfg(target_os = "windows")]
53 b':' => return true,
54 b'\\' => return true,
55 b'<' => return true,
56 b'>' => return true,
57 b'"' => return true,
58 b'|' => return true,
59 b'?' => return true,
60 b'*' => return true,
61 _ => (),
62 }
63 }
64 false
65}
66
67fn invalid_content(value: &[u8]) -> bool {
68 matches!(value, b"" | b"." | b"..")
69}
70
71fn normalize(this: &FileName) -> FileName {
72 *this
73}
74
75semantic_string! {
76 name: FileName,
79 capacity: FILENAME_LENGTH,
80 invalid_content: invalid_content,
81 invalid_characters: invalid_characters,
82 normalize: normalize
83}
84
85#[derive(Debug, Clone, Copy, Eq, Ord, PartialOrd, ZeroCopySend)]
86#[repr(C)]
87pub struct RestrictedFileName<const CAPACITY: usize> {
88 value: iceoryx2_bb_container::string::StaticString<CAPACITY>,
89}
90
91impl<const CAPACITY: usize>
92 iceoryx2_bb_container::semantic_string::internal::SemanticStringAccessor<CAPACITY>
93 for RestrictedFileName<CAPACITY>
94{
95 unsafe fn new_empty() -> Self {
96 static_assert_le::<{ CAPACITY }, { FILENAME_LENGTH }>();
97 static_assert_ge::<{ CAPACITY }, 1>();
98
99 Self {
100 value: iceoryx2_bb_container::string::StaticString::new(),
101 }
102 }
103
104 unsafe fn get_mut_string(
105 &mut self,
106 ) -> &mut iceoryx2_bb_container::string::StaticString<CAPACITY> {
107 &mut self.value
108 }
109
110 fn is_invalid_content(string: &[u8]) -> bool {
111 if Self::does_contain_invalid_characters(string) {
112 return true;
113 }
114
115 invalid_content(string)
116 }
117
118 fn does_contain_invalid_characters(string: &[u8]) -> bool {
119 if core::str::from_utf8(string).is_err() {
120 return true;
121 }
122
123 invalid_characters(string)
124 }
125}
126
127pub(crate) mod visitor_type {
129 pub(crate) struct RestrictedFileName<const CAPACITY: usize>;
130}
131
132impl<const CAPACITY: usize> serde::de::Visitor<'_> for visitor_type::RestrictedFileName<CAPACITY> {
133 type Value = RestrictedFileName<CAPACITY>;
134
135 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
136 formatter.write_str("a string containing the service name")
137 }
138
139 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
140 where
141 E: serde::de::Error,
142 {
143 RestrictedFileName::<CAPACITY>::new(v.as_bytes()).map_err(|e| {
144 E::custom(alloc::format!(
145 "invalid RestrictedFileName<{CAPACITY}> provided {v:?} ({e:?})."
146 ))
147 })
148 }
149}
150
151impl<'de, const CAPACITY: usize> serde::Deserialize<'de> for RestrictedFileName<CAPACITY> {
152 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153 where
154 D: serde::Deserializer<'de>,
155 {
156 deserializer.deserialize_str(visitor_type::RestrictedFileName::<CAPACITY>)
157 }
158}
159
160impl<const CAPACITY: usize> serde::Serialize for RestrictedFileName<CAPACITY> {
161 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162 where
163 S: serde::Serializer,
164 {
165 serializer.serialize_str(core::str::from_utf8(self.as_bytes()).unwrap())
166 }
167}
168impl<const CAPACITY: usize> iceoryx2_bb_container::semantic_string::SemanticString<CAPACITY>
171 for RestrictedFileName<CAPACITY>
172{
173 fn as_string(&self) -> &iceoryx2_bb_container::string::StaticString<CAPACITY> {
174 &self.value
175 }
176
177 fn normalize(&self) -> Self {
178 *self
179 }
180
181 unsafe fn new_unchecked(bytes: &[u8]) -> Self {
182 debug_assert!(bytes.len() <= CAPACITY);
183
184 Self {
185 value: iceoryx2_bb_container::string::StaticString::from_bytes_unchecked(bytes),
186 }
187 }
188
189 unsafe fn insert_bytes_unchecked(&mut self, idx: usize, bytes: &[u8]) {
190 debug_assert!(bytes.len() + self.len() <= CAPACITY);
191 use iceoryx2_bb_container::string::String;
192 self.value.insert_bytes_unchecked(idx, bytes);
193 }
194}
195
196impl<const CAPACITY: usize> RestrictedFileName<CAPACITY> {
197 pub const unsafe fn new_unchecked_const(value: &[u8]) -> RestrictedFileName<CAPACITY> {
205 debug_assert!(value.len() <= CAPACITY);
206 Self {
207 value: iceoryx2_bb_container::string::StaticString::from_bytes_unchecked(value),
208 }
209 }
210
211 pub const fn max_len() -> usize {
213 CAPACITY
214 }
215}
216
217impl<const CAPACITY: usize> core::fmt::Display for RestrictedFileName<CAPACITY> {
218 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
219 write!(f, "{}", self.value)
220 }
221}
222
223impl<const CAPACITY: usize> Hash for RestrictedFileName<CAPACITY> {
224 fn hash<H: Hasher>(&self, state: &mut H) {
225 self.normalize().as_bytes().hash(state)
226 }
227}
228
229impl<const CAPACITY: usize> From<RestrictedFileName<CAPACITY>> for FileName {
230 fn from(value: RestrictedFileName<CAPACITY>) -> FileName {
231 unsafe { FileName::new_unchecked(value.as_bytes()) }
234 }
235}
236
237impl<const CAPACITY: usize> From<RestrictedFileName<CAPACITY>> for String {
238 fn from(value: RestrictedFileName<CAPACITY>) -> String {
239 unsafe { String::from_utf8_unchecked(value.as_bytes().to_vec()) }
241 }
242}
243
244impl<const CAPACITY: usize> From<&RestrictedFileName<CAPACITY>> for String {
245 fn from(value: &RestrictedFileName<CAPACITY>) -> String {
246 unsafe { String::from_utf8_unchecked(value.as_bytes().to_vec()) }
248 }
249}
250
251impl<const CAPACITY: usize> core::convert::TryFrom<&str> for RestrictedFileName<CAPACITY> {
252 type Error = iceoryx2_bb_container::semantic_string::SemanticStringError;
253
254 fn try_from(value: &str) -> Result<Self, Self::Error> {
255 Self::new(value.as_bytes())
256 }
257}
258
259impl<const CAPACITY: usize> core::convert::TryFrom<&FileName> for RestrictedFileName<CAPACITY> {
260 type Error = iceoryx2_bb_container::semantic_string::SemanticStringError;
261
262 fn try_from(value: &FileName) -> Result<Self, Self::Error> {
263 Self::new(value.as_bytes())
264 }
265}
266
267impl<const CAPACITY: usize> PartialEq<RestrictedFileName<CAPACITY>>
268 for RestrictedFileName<CAPACITY>
269{
270 fn eq(&self, other: &RestrictedFileName<CAPACITY>) -> bool {
271 *self.normalize().as_bytes() == *other.normalize().as_bytes()
272 }
273}
274
275impl<const CAPACITY: usize> PartialEq<&[u8]> for RestrictedFileName<CAPACITY> {
276 fn eq(&self, other: &&[u8]) -> bool {
277 let other = match RestrictedFileName::<CAPACITY>::new(other) {
278 Ok(other) => other,
279 Err(_) => return false,
280 };
281
282 *self == other
283 }
284}
285
286impl<const CAPACITY: usize> PartialEq<&[u8]> for &RestrictedFileName<CAPACITY> {
287 fn eq(&self, other: &&[u8]) -> bool {
288 let other = match RestrictedFileName::<CAPACITY>::new(other) {
289 Ok(other) => other,
290 Err(_) => return false,
291 };
292
293 **self == other
294 }
295}
296
297impl<const CAPACITY: usize> PartialEq<[u8; CAPACITY]> for RestrictedFileName<CAPACITY> {
298 fn eq(&self, other: &[u8; CAPACITY]) -> bool {
299 let other = match RestrictedFileName::<CAPACITY>::new(other) {
300 Ok(other) => other,
301 Err(_) => return false,
302 };
303
304 *self == other
305 }
306}
307
308impl<const CAPACITY: usize> PartialEq<&[u8; CAPACITY]> for RestrictedFileName<CAPACITY> {
309 fn eq(&self, other: &&[u8; CAPACITY]) -> bool {
310 #[allow(clippy::explicit_auto_deref)]
312 let other = match RestrictedFileName::<CAPACITY>::new(*other) {
313 Ok(other) => other,
314 Err(_) => return false,
315 };
316
317 *self == other
318 }
319}
320
321impl<const CAPACITY: usize> core::ops::Deref for RestrictedFileName<CAPACITY> {
322 type Target = [u8];
323
324 fn deref(&self) -> &Self::Target {
325 use iceoryx2_bb_container::string::String;
326 self.value.as_bytes()
327 }
328}