Skip to main content

iceoryx2_cal/
named_concept.rs

1// Copyright (c) 2023 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13use core::fmt::Debug;
14
15use alloc::format;
16use alloc::vec::Vec;
17
18use iceoryx2_bb_container::semantic_string::SemanticString;
19use iceoryx2_bb_posix::config::TEMP_DIRECTORY;
20use iceoryx2_bb_posix::directory::{Directory, DirectoryRemoveError};
21pub use iceoryx2_bb_system_types::file_name::FileName;
22pub use iceoryx2_bb_system_types::file_path::FilePath;
23pub use iceoryx2_bb_system_types::path::Path;
24use iceoryx2_log::{fail, fatal_panic};
25
26#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
27pub enum NamedConceptDoesExistError {
28    InsufficientPermissions,
29    UnderlyingResourcesBeingSetUp,
30    UnderlyingResourcesCorrupted,
31    InternalError,
32}
33
34impl core::fmt::Display for NamedConceptDoesExistError {
35    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
36        write!(f, "NamedConceptDoesExistError::{self:?}")
37    }
38}
39
40impl core::error::Error for NamedConceptDoesExistError {}
41
42#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
43pub enum NamedConceptRemoveError {
44    InsufficientPermissions,
45    InternalError,
46}
47
48impl core::fmt::Display for NamedConceptRemoveError {
49    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50        write!(f, "NamedConceptRemoveError::{self:?}")
51    }
52}
53
54impl core::error::Error for NamedConceptRemoveError {}
55
56#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
57pub enum NamedConceptListError {
58    InsufficientPermissions,
59    InternalError,
60}
61
62impl core::fmt::Display for NamedConceptListError {
63    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
64        write!(f, "NamedConceptListError::{self:?}")
65    }
66}
67
68impl core::error::Error for NamedConceptListError {}
69
70#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
71pub enum NamedConceptPathHintRemoveError {
72    InsufficientPermissions,
73    InternalError,
74}
75
76impl core::fmt::Display for NamedConceptPathHintRemoveError {
77    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
78        write!(f, "NamedConceptPathHintRemoveError::{self:?}")
79    }
80}
81
82impl core::error::Error for NamedConceptPathHintRemoveError {}
83
84/// Every [`NamedConcept`] must have a custom configuration that at least allows the user to define
85/// a custom [`NamedConceptConfiguration::suffix()`] for all file names that are transparent during
86/// usage as well as a [`NamedConceptConfiguration::path_hint()`] that can be ignored if the
87/// underlying resource does not support it.
88pub trait NamedConceptConfiguration: Default + Clone + Debug + Send {
89    /// Defines the prefix that the concept will use.
90    fn prefix(self, value: &FileName) -> Self;
91
92    /// Returns the configurations prefix.
93    fn get_prefix(&self) -> &FileName;
94
95    /// Defines the suffix that the concept will use.
96    fn suffix(self, value: &FileName) -> Self;
97
98    /// Sets a path hint under which the underlying resources shall be stored. When the concept
99    /// uses resources like [`iceoryx2_bb_posix::shared_memory::SharedMemory`] the path will be
100    /// ignored.
101    fn path_hint(self, value: &Path) -> Self;
102
103    /// Returns the configurations suffix.
104    fn get_suffix(&self) -> &FileName;
105
106    /// Returns the configurations path hint.
107    fn get_path_hint(&self) -> &Path;
108
109    /// Returns the full path for a given value under the given configuration.
110    fn path_for(&self, value: &FileName) -> FilePath {
111        let mut path = *self.get_path_hint();
112        fatal_panic!(from self, when path.add_path_entry(&self.get_prefix().into()),
113                    "The path hint \"{}\" in combination with the prefix \"{}\" exceed the maximum supported path length of {} of the operating system.",
114                    path, value, Path::max_len());
115        fatal_panic!(from self, when path.push_bytes(value.as_string()),
116                    "The path hint \"{}\" in combination with the file name \"{}\" exceed the maximum supported path length of {} of the operating system.",
117                    path, value, Path::max_len());
118        fatal_panic!(from self, when path.push_bytes(self.get_suffix()),
119                    "The path hint \"{}\" in combination with the file name \"{}\" and the suffix \"{}\" exceed the maximum supported path length of {} of the operating system.",
120                    path, value, self.get_suffix(), Path::max_len());
121
122        unsafe { FilePath::new_unchecked(path.as_bytes()) }
123    }
124
125    /// Extracts the name from a full path under a given configuration.
126    fn extract_name_from_path(&self, value: &FilePath) -> Option<FileName> {
127        if *self.get_path_hint() != value.path() {
128            return None;
129        }
130
131        self.extract_name_from_file(&value.file_name())
132    }
133
134    /// Extracts the name from a file name under a given configuration.
135    fn extract_name_from_file(&self, value: &FileName) -> Option<FileName> {
136        let mut file = *value;
137
138        if !fatal_panic!(from self, when file.strip_prefix(self.get_prefix().as_bytes()),
139                    "Stripping the prefix \"{}\" from the file name \"{}\" leads to invalid content.",
140                    self.get_prefix(), file)
141        {
142            return None;
143        }
144
145        if !fatal_panic!(from self, when file.strip_suffix(self.get_suffix().as_bytes()),
146                    "Stripping the suffix \"{}\" from the file name \"{}\" leads to invalid content.",
147                    self.get_suffix(), file)
148        {
149            return None;
150        }
151
152        Some(file)
153    }
154}
155
156/// Builder trait to create new [`NamedConcept`]s.
157pub trait NamedConceptBuilder<T: NamedConceptMgmt> {
158    /// Defines the name of the newly created [`NamedConcept`].
159    fn new(name: &FileName) -> Self;
160
161    /// Sets the custom configuration of the concept.
162    fn config(self, config: &T::Configuration) -> Self;
163}
164
165/// Every concept that is uniquely identified by a [`FileName`] and corresponds to some kind of
166/// file in the file system is a [`NamedConcept`]. This trait provides the essential property of
167/// these concepts [`NamedConcept::name()`]
168pub trait NamedConcept: Debug {
169    /// Returns the name of the concept
170    fn name(&self) -> &FileName;
171}
172
173/// Every concept that is uniquely identified by a [`FileName`] and corresponds to some kind of
174/// file in the file system is a [`NamedConcept`]. This trait provides common management methods
175/// for such concepts, like
176///  * [`NamedConceptMgmt::remove()`]
177///  * [`NamedConceptMgmt::does_exist()`]
178///  * [`NamedConceptMgmt::list()`]
179pub trait NamedConceptMgmt: Debug {
180    type Configuration: NamedConceptConfiguration;
181
182    /// Removes an existing concept. Returns true if the concepts existed and was removed,
183    /// if the concept did not exist it returns false.
184    ///
185    /// # Safety
186    ///
187    ///  * It must be ensured that no other process is using the concept.
188    ///
189    unsafe fn remove(name: &FileName) -> Result<bool, NamedConceptRemoveError> {
190        unsafe { Self::remove_cfg(name, &Self::Configuration::default()) }
191    }
192
193    /// Returns true if a concept with that name exists, otherwise false
194    fn does_exist(name: &FileName) -> Result<bool, NamedConceptDoesExistError> {
195        Self::does_exist_cfg(name, &Self::Configuration::default())
196    }
197
198    /// Returns a list of all available concepts with the default configuration.
199    fn list() -> Result<Vec<FileName>, NamedConceptListError> {
200        Self::list_cfg(&Self::Configuration::default())
201    }
202
203    /// Removes an existing concept under a custom configuration. Returns true if the concepts
204    /// existed and was removed, if the concept did not exist it returns false.
205    ///
206    /// # Safety
207    ///
208    ///  * It must be ensured that no other process is using the concept.
209    ///
210    unsafe fn remove_cfg(
211        name: &FileName,
212        cfg: &Self::Configuration,
213    ) -> Result<bool, NamedConceptRemoveError>;
214
215    /// Returns true if a concept with that name exists under a custom configuration, otherwise false
216    fn does_exist_cfg(
217        name: &FileName,
218        cfg: &Self::Configuration,
219    ) -> Result<bool, NamedConceptDoesExistError>;
220
221    /// Returns a list of all available concepts with a custom configuration.
222    fn list_cfg(cfg: &Self::Configuration) -> Result<Vec<FileName>, NamedConceptListError>;
223
224    /// The default prefix of every zero copy connection
225    fn default_prefix() -> FileName {
226        unsafe { FileName::new_unchecked(b"iox2_") }
227    }
228
229    /// The default path hint for every zero copy connection
230    fn default_path_hint() -> Path {
231        TEMP_DIRECTORY
232    }
233
234    /// Removes the path hint directory. Will be realized only when the concept actually uses
235    /// the path hint.
236    fn remove_path_hint(value: &Path) -> Result<(), NamedConceptPathHintRemoveError>;
237}
238
239pub(crate) fn remove_path_hint(value: &Path) -> Result<(), NamedConceptPathHintRemoveError> {
240    let origin = format!("remove_path_hint({value:?})");
241    let msg = "Unable to remove path hint";
242    match Directory::remove_empty(value) {
243        Ok(()) | Err(DirectoryRemoveError::DirectoryDoesNotExist) => Ok(()),
244        Err(DirectoryRemoveError::InsufficientPermissions) => {
245            fail!(from origin, with NamedConceptPathHintRemoveError::InsufficientPermissions,
246                "{} due to insufficient permissions.", msg);
247        }
248        Err(e) => {
249            fail!(from origin, with NamedConceptPathHintRemoveError::InternalError,
250                "{} due to an internal error ({:?}).", msg, e);
251        }
252    }
253}