ferroui_base/input/
data_format.rs1use super::{DataFormatKind, DataFormatOf};
2use crate::media::imaging::Bitmap;
3use crate::platform::storage::IStorageItem;
4use std::any::TypeId;
5use std::fmt;
6use std::hash::{Hash, Hasher};
7use std::rc::Rc;
8
9#[derive(Clone)]
15pub struct DataFormat {
16 kind: DataFormatKind,
17 identifier: Rc<str>,
18 data_type: Option<TypeId>,
22}
23
24impl PartialEq for DataFormat {
25 fn eq(&self, other: &DataFormat) -> bool {
26 self.kind == other.kind && self.identifier == other.identifier
27 }
28}
29
30impl Eq for DataFormat {}
31
32impl Hash for DataFormat {
33 fn hash<H: Hasher>(&self, state: &mut H) {
34 self.kind.hash(state);
35 self.identifier.hash(state);
36 }
37}
38
39crate::ferro_thread_local! {
40 static TEXT: DataFormatOf<String> = DataFormat::create_universal_format("Text");
41 static BITMAP: DataFormatOf<Rc<Bitmap>> = DataFormat::create_universal_format("Bitmap");
42 static FILE: DataFormatOf<Rc<dyn IStorageItem>> = DataFormat::create_universal_format("File");
43}
44
45impl DataFormat {
46 pub(super) fn new(kind: DataFormatKind, identifier: &str, data_type: Option<TypeId>) -> Self {
47 Self { kind, identifier: Rc::from(identifier), data_type }
48 }
49
50 #[inline]
57 pub fn data_type(&self) -> Option<TypeId> {
58 self.data_type
59 }
60
61 #[inline]
63 pub fn kind(&self) -> DataFormatKind {
64 self.kind
65 }
66
67 #[inline]
69 pub fn identifier(&self) -> &str {
70 &self.identifier
71 }
72
73 pub fn text() -> DataFormatOf<String> {
75 TEXT.with(Clone::clone)
76 }
77
78 pub fn bitmap() -> DataFormatOf<Rc<Bitmap>> {
81 BITMAP.with(Clone::clone)
82 }
83
84 pub fn file() -> DataFormatOf<Rc<dyn IStorageItem>> {
87 FILE.with(Clone::clone)
88 }
89
90 pub fn to_system_name(&self, application_prefix: &str) -> String {
99 match self.kind {
100 DataFormatKind::Application => {
101 let mut name = String::with_capacity(application_prefix.len() + self.identifier.len());
102 name.push_str(application_prefix);
103 name.push_str(&self.identifier);
104 name
105 }
106 DataFormatKind::Platform => self.identifier.to_string(),
107 _ => panic!("Cannot get system name for {} format {}", self.kind, self.identifier),
108 }
109 }
110
111 fn create_universal_format<T: 'static>(identifier: &str) -> DataFormatOf<T> {
112 DataFormatOf::new(DataFormatKind::Universal, identifier)
113 }
114
115 pub fn create_bytes_application_format(identifier: &str) -> DataFormatOf<Rc<[u8]>> {
129 Self::create_application_format(identifier)
130 }
131
132 pub fn create_string_application_format(identifier: &str) -> DataFormatOf<String> {
138 Self::create_application_format(identifier)
139 }
140
141 pub fn create_in_process_format<T: 'static>(identifier: &str) -> DataFormatOf<T> {
151 if identifier.is_empty() {
152 panic!("The value cannot be an empty string. (Parameter 'identifier')");
153 }
154
155 DataFormatOf::new(DataFormatKind::InProcess, identifier)
156 }
157
158 fn create_application_format<T: 'static>(identifier: &str) -> DataFormatOf<T> {
159 if !Self::is_valid_application_format_identifier(identifier) {
160 panic!("Invalid application identifier (Parameter 'identifier')");
161 }
162
163 DataFormatOf::new(DataFormatKind::Application, identifier)
164 }
165
166 pub fn create_bytes_platform_format(identifier: &str) -> DataFormatOf<Rc<[u8]>> {
176 Self::create_platform_format(identifier)
177 }
178
179 pub fn create_string_platform_format(identifier: &str) -> DataFormatOf<String> {
185 Self::create_platform_format(identifier)
186 }
187
188 fn create_platform_format<T: 'static>(identifier: &str) -> DataFormatOf<T> {
189 if identifier.is_empty() {
190 panic!("The value cannot be an empty string. (Parameter 'identifier')");
191 }
192
193 DataFormatOf::new(DataFormatKind::Platform, identifier)
194 }
195
196 pub fn from_system_name<T: 'static>(system_name: &str, application_prefix: &str) -> DataFormatOf<T> {
202 let prefix_length = application_prefix.len();
203 let has_prefix = system_name
204 .as_bytes()
205 .get(..prefix_length)
206 .is_some_and(|start| start.eq_ignore_ascii_case(application_prefix.as_bytes()))
207 && system_name.is_char_boundary(prefix_length);
208
209 if has_prefix {
210 let identifier = &system_name[prefix_length..];
211 if Self::is_valid_application_format_identifier(identifier) {
212 return DataFormatOf::new(DataFormatKind::Application, identifier);
213 }
214 }
215
216 DataFormatOf::new(DataFormatKind::Platform, system_name)
217 }
218
219 fn is_valid_application_format_identifier(identifier: &str) -> bool {
220 !identifier.is_empty() && identifier.chars().all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-')
221 }
222}
223
224impl fmt::Display for DataFormat {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 write!(f, "{}: {}", self.kind, self.identifier)
227 }
228}
229
230impl fmt::Debug for DataFormat {
231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232 fmt::Display::fmt(self, f)
233 }
234}