Skip to main content

azul_css/
corety.rs

1use alloc::{
2    string::{String, ToString},
3    vec::Vec,
4};
5
6use crate::props::basic::ColorU;
7
8// ============================================================================
9// Void type - FFI-safe replacement for ()
10// ============================================================================
11
12/// FFI-safe void type to replace `()` in Result types.
13/// 
14/// Since `()` (unit type) has zero size, it's not FFI-safe.
15/// This type provides a minimal 1-byte representation that can be
16/// safely passed across the C ABI boundary.
17/// 
18/// # Usage
19/// Instead of `Result<(), Error>`, use `Result<Void, Error>`.
20/// 
21/// # Example
22/// ```ignore
23/// fn do_something() -> Result<Void, MyError> {
24///     // ... do work ...
25///     Ok(Void::default())
26/// }
27/// ```
28#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29#[repr(C)]
30pub struct Void {
31    /// Reserved byte to ensure the struct has non-zero size.
32    /// Always initialized to 0.
33    pub _reserved: u8,
34}
35
36impl Default for Void {
37    fn default() -> Self {
38        Self { _reserved: 0 }
39    }
40}
41
42impl Void {
43    /// Create a new Void value (equivalent to `()`)
44    pub const fn new() -> Self {
45        Self { _reserved: 0 }
46    }
47}
48
49impl From<()> for Void {
50    fn from(_: ()) -> Self {
51        Self::default()
52    }
53}
54
55impl From<Void> for () {
56    fn from(_: Void) -> Self {
57        ()
58    }
59}
60
61// ============================================================================
62// Debug message types
63// ============================================================================
64
65// Debug message severity/category
66#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
67#[repr(C)]
68pub enum LayoutDebugMessageType {
69    Info,
70    Warning,
71    Error,
72    // Layout-specific categories for filtering
73    BoxProps,
74    CssGetter,
75    BfcLayout,
76    IfcLayout,
77    TableLayout,
78    DisplayType,
79    PositionCalculation,
80}
81
82impl Default for LayoutDebugMessageType {
83    fn default() -> Self {
84        Self::Info
85    }
86}
87
88// Define a struct for debug messages
89#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
90#[repr(C)]
91pub struct LayoutDebugMessage {
92    pub message_type: LayoutDebugMessageType,
93    pub message: AzString,
94    pub location: AzString,
95}
96
97impl LayoutDebugMessage {
98    /// Create a new debug message with automatic caller location tracking
99    #[track_caller]
100    pub fn new(message_type: LayoutDebugMessageType, message: impl Into<String>) -> Self {
101        let location = core::panic::Location::caller();
102        Self {
103            message_type,
104            message: AzString::from_string(message.into()),
105            location: AzString::from_string(format!(
106                "{}:{}:{}",
107                location.file(),
108                location.line(),
109                location.column()
110            )),
111        }
112    }
113
114    /// Helper for Info messages
115    #[track_caller]
116    pub fn info(message: impl Into<String>) -> Self {
117        Self::new(LayoutDebugMessageType::Info, message)
118    }
119
120    /// Helper for Warning messages
121    #[track_caller]
122    pub fn warning(message: impl Into<String>) -> Self {
123        Self::new(LayoutDebugMessageType::Warning, message)
124    }
125
126    /// Helper for Error messages
127    #[track_caller]
128    pub fn error(message: impl Into<String>) -> Self {
129        Self::new(LayoutDebugMessageType::Error, message)
130    }
131
132    /// Helper for BoxProps debug messages
133    #[track_caller]
134    pub fn box_props(message: impl Into<String>) -> Self {
135        Self::new(LayoutDebugMessageType::BoxProps, message)
136    }
137
138    /// Helper for CSS Getter debug messages
139    #[track_caller]
140    pub fn css_getter(message: impl Into<String>) -> Self {
141        Self::new(LayoutDebugMessageType::CssGetter, message)
142    }
143
144    /// Helper for BFC Layout debug messages
145    #[track_caller]
146    pub fn bfc_layout(message: impl Into<String>) -> Self {
147        Self::new(LayoutDebugMessageType::BfcLayout, message)
148    }
149
150    /// Helper for IFC Layout debug messages
151    #[track_caller]
152    pub fn ifc_layout(message: impl Into<String>) -> Self {
153        Self::new(LayoutDebugMessageType::IfcLayout, message)
154    }
155
156    /// Helper for Table Layout debug messages
157    #[track_caller]
158    pub fn table_layout(message: impl Into<String>) -> Self {
159        Self::new(LayoutDebugMessageType::TableLayout, message)
160    }
161
162    /// Helper for Display Type debug messages
163    #[track_caller]
164    pub fn display_type(message: impl Into<String>) -> Self {
165        Self::new(LayoutDebugMessageType::DisplayType, message)
166    }
167}
168
169#[repr(C)]
170pub struct AzString {
171    pub vec: U8Vec,
172}
173
174impl_option!(
175    AzString,
176    OptionString,
177    copy = false,
178    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
179);
180
181static DEFAULT_STR: &str = "";
182
183impl Default for AzString {
184    fn default() -> Self {
185        DEFAULT_STR.into()
186    }
187}
188
189impl<'a> From<&'a str> for AzString {
190    fn from(s: &'a str) -> Self {
191        s.to_string().into()
192    }
193}
194
195impl AsRef<str> for AzString {
196    fn as_ref<'a>(&'a self) -> &'a str {
197        self.as_str()
198    }
199}
200
201impl core::fmt::Debug for AzString {
202    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
203        self.as_str().fmt(f)
204    }
205}
206
207impl core::fmt::Display for AzString {
208    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
209        self.as_str().fmt(f)
210    }
211}
212
213impl AzString {
214    #[inline]
215    pub const fn from_const_str(s: &'static str) -> Self {
216        Self {
217            vec: U8Vec::from_const_slice(s.as_bytes()),
218        }
219    }
220
221    /// Creates a new AzString from a null-terminated C string (const char*).
222    /// This copies the string data into a new allocation.
223    ///
224    /// # Safety
225    /// - `ptr` must be a valid pointer to a null-terminated UTF-8 string
226    /// - The string must remain valid for the duration of this call
227    #[inline]
228    pub unsafe fn from_c_str(ptr: *const i8) -> Self {
229        if ptr.is_null() {
230            return Self::default();
231        }
232        let c_str = core::ffi::CStr::from_ptr(ptr);
233        let bytes = c_str.to_bytes();
234        Self::copy_from_bytes(bytes.as_ptr(), 0, bytes.len())
235    }
236
237    /// Copies bytes from a pointer into a new AzString.
238    /// This is useful for C FFI where you have a char* buffer.
239    #[inline]
240    pub fn copy_from_bytes(ptr: *const u8, start: usize, len: usize) -> Self {
241        Self {
242            vec: U8Vec::copy_from_bytes(ptr, start, len),
243        }
244    }
245
246    #[inline]
247    pub fn from_string(s: String) -> Self {
248        Self {
249            vec: U8Vec::from_vec(s.into_bytes()),
250        }
251    }
252
253    #[inline]
254    pub fn as_str(&self) -> &str {
255        unsafe { core::str::from_utf8_unchecked(self.vec.as_ref()) }
256    }
257
258    /// NOTE: CLONES the memory if the memory is external or &'static
259    /// Moves the memory out if the memory is library-allocated
260    #[inline]
261    pub fn clone_self(&self) -> Self {
262        Self {
263            vec: self.vec.clone_self(),
264        }
265    }
266
267    #[inline]
268    pub fn into_library_owned_string(self) -> String {
269        match self.vec.destructor {
270            U8VecDestructor::NoDestructor | U8VecDestructor::External(_) => {
271                self.as_str().to_string()
272            }
273            U8VecDestructor::DefaultRust => {
274                let m = core::mem::ManuallyDrop::new(self);
275                unsafe { String::from_raw_parts(m.vec.ptr as *mut u8, m.vec.len, m.vec.cap) }
276            }
277        }
278    }
279
280    #[inline]
281    pub fn as_bytes(&self) -> &[u8] {
282        self.vec.as_ref()
283    }
284
285    #[inline]
286    pub fn into_bytes(self) -> U8Vec {
287        let m = core::mem::ManuallyDrop::new(self);
288        U8Vec {
289            ptr: m.vec.ptr,
290            len: m.vec.len,
291            cap: m.vec.cap,
292            destructor: m.vec.destructor,
293            run_destructor: m.vec.run_destructor,
294        }
295    }
296
297    /// Returns the length of the string in bytes (not including null terminator)
298    #[inline]
299    pub fn len(&self) -> usize {
300        self.vec.len
301    }
302
303    /// Returns true if the string is empty
304    #[inline]
305    pub fn is_empty(&self) -> bool {
306        self.vec.len == 0
307    }
308
309    /// Creates a null-terminated copy of the string for C FFI usage.
310    /// Returns a new U8Vec that contains the string data followed by a null byte.
311    /// The caller is responsible for freeing this memory.
312    ///
313    /// Use this when you need to pass a string to C code that expects `const char*`.
314    #[inline]
315    pub fn to_c_str(&self) -> U8Vec {
316        let bytes = self.as_bytes();
317        let mut result = Vec::with_capacity(bytes.len() + 1);
318        result.extend_from_slice(bytes);
319        result.push(0); // null terminator
320        U8Vec::from_vec(result)
321    }
322
323    /// Creates a new AzString from UTF-16 encoded bytes (little-endian).
324    /// Returns an empty string if the input is invalid UTF-16 or has odd length.
325    ///
326    /// # Arguments
327    /// * `ptr` - Pointer to UTF-16 encoded bytes
328    /// * `len` - Length in bytes (not code units) - must be even
329    ///
330    /// # Safety
331    /// - `ptr` must be valid for reading `len` bytes
332    /// - `len` must be even (UTF-16 uses 2 bytes per code unit)
333    #[inline]
334    pub unsafe fn from_utf16_le(ptr: *const u8, len: usize) -> Self {
335        if ptr.is_null() || len == 0 {
336            return Self::default();
337        }
338        
339        // UTF-16 requires pairs of bytes
340        if len % 2 != 0 {
341            return Self::default();
342        }
343        
344        let byte_slice = core::slice::from_raw_parts(ptr, len);
345        let code_units: Vec<u16> = byte_slice
346            .chunks_exact(2)
347            .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
348            .collect();
349        
350        match String::from_utf16(&code_units) {
351            Ok(s) => Self::from_string(s),
352            Err(_) => Self::default(),
353        }
354    }
355
356    /// Creates a new AzString from UTF-16 encoded bytes (big-endian).
357    /// Returns an empty string if the input is invalid UTF-16 or has odd length.
358    ///
359    /// # Arguments
360    /// * `ptr` - Pointer to UTF-16 encoded bytes
361    /// * `len` - Length in bytes (not code units) - must be even
362    ///
363    /// # Safety
364    /// - `ptr` must be valid for reading `len` bytes
365    /// - `len` must be even (UTF-16 uses 2 bytes per code unit)
366    #[inline]
367    pub unsafe fn from_utf16_be(ptr: *const u8, len: usize) -> Self {
368        if ptr.is_null() || len == 0 {
369            return Self::default();
370        }
371        
372        // UTF-16 requires pairs of bytes
373        if len % 2 != 0 {
374            return Self::default();
375        }
376        
377        let byte_slice = core::slice::from_raw_parts(ptr, len);
378        let code_units: Vec<u16> = byte_slice
379            .chunks_exact(2)
380            .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
381            .collect();
382        
383        match String::from_utf16(&code_units) {
384            Ok(s) => Self::from_string(s),
385            Err(_) => Self::default(),
386        }
387    }
388
389    /// Creates a new AzString from UTF-8 bytes with lossy conversion.
390    /// Invalid UTF-8 sequences are replaced with the Unicode replacement character (U+FFFD).
391    ///
392    /// # Safety
393    /// - `ptr` must be valid for reading `len` bytes
394    #[inline]
395    pub unsafe fn from_utf8_lossy(ptr: *const u8, len: usize) -> Self {
396        if ptr.is_null() || len == 0 {
397            return Self::default();
398        }
399        
400        let byte_slice = core::slice::from_raw_parts(ptr, len);
401        let s = String::from_utf8_lossy(byte_slice).into_owned();
402        Self::from_string(s)
403    }
404
405    /// Creates a new AzString from UTF-8 bytes.
406    /// Returns an empty string if the input is not valid UTF-8.
407    ///
408    /// # Safety
409    /// - `ptr` must be valid for reading `len` bytes
410    #[inline]
411    pub unsafe fn from_utf8(ptr: *const u8, len: usize) -> Self {
412        if ptr.is_null() || len == 0 {
413            return Self::default();
414        }
415        
416        let byte_slice = core::slice::from_raw_parts(ptr, len);
417        match core::str::from_utf8(byte_slice) {
418            Ok(s) => Self::from_string(s.to_string()),
419            Err(_) => Self::default(),
420        }
421    }
422}
423
424impl From<String> for AzString {
425    fn from(input: String) -> AzString {
426        AzString::from_string(input)
427    }
428}
429
430impl PartialOrd for AzString {
431    fn partial_cmp(&self, rhs: &Self) -> Option<core::cmp::Ordering> {
432        self.as_str().partial_cmp(rhs.as_str())
433    }
434}
435
436impl Ord for AzString {
437    fn cmp(&self, rhs: &Self) -> core::cmp::Ordering {
438        self.as_str().cmp(rhs.as_str())
439    }
440}
441
442impl Clone for AzString {
443    fn clone(&self) -> Self {
444        self.clone_self()
445    }
446}
447
448impl PartialEq for AzString {
449    fn eq(&self, rhs: &Self) -> bool {
450        self.as_str().eq(rhs.as_str())
451    }
452}
453
454impl Eq for AzString {}
455
456impl core::hash::Hash for AzString {
457    fn hash<H>(&self, state: &mut H)
458    where
459        H: core::hash::Hasher,
460    {
461        self.as_str().hash(state)
462    }
463}
464
465impl core::ops::Deref for AzString {
466    type Target = str;
467
468    fn deref(&self) -> &str {
469        self.as_str()
470    }
471}
472
473impl_vec!(u8, U8Vec, U8VecDestructor, U8VecDestructorType);
474impl_vec_mut!(u8, U8Vec);
475impl_vec_debug!(u8, U8Vec);
476impl_vec_partialord!(u8, U8Vec);
477impl_vec_ord!(u8, U8Vec);
478impl_vec_clone!(u8, U8Vec, U8VecDestructor);
479impl_vec_partialeq!(u8, U8Vec);
480impl_vec_eq!(u8, U8Vec);
481impl_vec_hash!(u8, U8Vec);
482
483impl U8Vec {
484    /// Copies bytes from a pointer into a new Vec.
485    /// This is useful for C FFI where you have a uint8_t* buffer.
486    #[inline]
487    pub fn copy_from_bytes(ptr: *const u8, start: usize, len: usize) -> Self {
488        if ptr.is_null() || len == 0 {
489            return Self::new();
490        }
491        let slice = unsafe { core::slice::from_raw_parts(ptr.add(start), len) };
492        Self::from_vec(slice.to_vec())
493    }
494}
495
496impl_option!(
497    U8Vec,
498    OptionU8Vec,
499    copy = false,
500    [Debug, Clone, PartialEq, Ord, PartialOrd, Eq, Hash]
501);
502
503impl_vec!(u16, U16Vec, U16VecDestructor, U16VecDestructorType);
504impl_vec_debug!(u16, U16Vec);
505impl_vec_partialord!(u16, U16Vec);
506impl_vec_ord!(u16, U16Vec);
507impl_vec_clone!(u16, U16Vec, U16VecDestructor);
508impl_vec_partialeq!(u16, U16Vec);
509impl_vec_eq!(u16, U16Vec);
510impl_vec_hash!(u16, U16Vec);
511
512impl_vec!(f32, F32Vec, F32VecDestructor, F32VecDestructorType);
513impl_vec_debug!(f32, F32Vec);
514impl_vec_partialord!(f32, F32Vec);
515impl_vec_clone!(f32, F32Vec, F32VecDestructor);
516impl_vec_partialeq!(f32, F32Vec);
517
518// Vec<char>
519impl_vec!(u32, U32Vec, U32VecDestructor, U32VecDestructorType);
520impl_vec_mut!(u32, U32Vec);
521impl_vec_debug!(u32, U32Vec);
522impl_vec_partialord!(u32, U32Vec);
523impl_vec_ord!(u32, U32Vec);
524impl_vec_clone!(u32, U32Vec, U32VecDestructor);
525impl_vec_partialeq!(u32, U32Vec);
526impl_vec_eq!(u32, U32Vec);
527impl_vec_hash!(u32, U32Vec);
528
529impl_vec!(
530    AzString,
531    StringVec,
532    StringVecDestructor,
533    StringVecDestructorType
534);
535impl_vec_debug!(AzString, StringVec);
536impl_vec_partialord!(AzString, StringVec);
537impl_vec_ord!(AzString, StringVec);
538impl_vec_clone!(AzString, StringVec, StringVecDestructor);
539impl_vec_partialeq!(AzString, StringVec);
540impl_vec_eq!(AzString, StringVec);
541impl_vec_hash!(AzString, StringVec);
542
543impl From<Vec<String>> for StringVec {
544    fn from(v: Vec<String>) -> StringVec {
545        let new_v: Vec<AzString> = v.into_iter().map(|s| s.into()).collect();
546        new_v.into()
547    }
548}
549
550impl_option!(
551    StringVec,
552    OptionStringVec,
553    copy = false,
554    [Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash]
555);
556
557impl_option!(
558    u16,
559    OptionU16,
560    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
561);
562impl_option!(
563    u32,
564    OptionU32,
565    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
566);
567impl_option!(
568    u64,
569    OptionU64,
570    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
571);
572impl_option!(
573    usize,
574    OptionUsize,
575    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
576);
577impl_option!(
578    i16,
579    OptionI16,
580    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
581);
582impl_option!(
583    i32,
584    OptionI32,
585    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
586);
587impl_option!(f32, OptionF32, [Debug, Copy, Clone, PartialEq, PartialOrd]);
588impl_option!(f64, OptionF64, [Debug, Copy, Clone, PartialEq, PartialOrd]);
589
590// Manual implementations for Hash and Ord on OptionF32 (since f32 doesn't implement these traits)
591impl core::hash::Hash for OptionF32 {
592    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
593        match self {
594            OptionF32::None => 0u8.hash(state),
595            OptionF32::Some(v) => {
596                1u8.hash(state);
597                v.to_bits().hash(state);
598            }
599        }
600    }
601}
602
603impl Eq for OptionF32 {}
604
605impl Ord for OptionF32 {
606    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
607        match (self, other) {
608            (OptionF32::None, OptionF32::None) => core::cmp::Ordering::Equal,
609            (OptionF32::None, OptionF32::Some(_)) => core::cmp::Ordering::Less,
610            (OptionF32::Some(_), OptionF32::None) => core::cmp::Ordering::Greater,
611            (OptionF32::Some(a), OptionF32::Some(b)) => {
612                a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal)
613            }
614        }
615    }
616}