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
181/// Type alias for compatibility - OptionAzString is the same as OptionString
182pub type OptionAzString = OptionString;
183
184static DEFAULT_STR: &str = "";
185
186impl Default for AzString {
187    fn default() -> Self {
188        DEFAULT_STR.into()
189    }
190}
191
192impl<'a> From<&'a str> for AzString {
193    fn from(s: &'a str) -> Self {
194        s.to_string().into()
195    }
196}
197
198impl AsRef<str> for AzString {
199    fn as_ref<'a>(&'a self) -> &'a str {
200        self.as_str()
201    }
202}
203
204impl core::fmt::Debug for AzString {
205    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
206        self.as_str().fmt(f)
207    }
208}
209
210impl core::fmt::Display for AzString {
211    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
212        self.as_str().fmt(f)
213    }
214}
215
216impl AzString {
217    #[inline]
218    pub const fn from_const_str(s: &'static str) -> Self {
219        Self {
220            vec: U8Vec::from_const_slice(s.as_bytes()),
221        }
222    }
223
224    /// Creates a new AzString from a null-terminated C string (const char*).
225    /// This copies the string data into a new allocation.
226    ///
227    /// # Safety
228    /// - `ptr` must be a valid pointer to a null-terminated UTF-8 string
229    /// - The string must remain valid for the duration of this call
230    #[inline]
231    pub unsafe fn from_c_str(ptr: *const core::ffi::c_char) -> Self {
232        if ptr.is_null() {
233            return Self::default();
234        }
235        let c_str = core::ffi::CStr::from_ptr(ptr);
236        let bytes = c_str.to_bytes();
237        Self::copy_from_bytes(bytes.as_ptr(), 0, bytes.len())
238    }
239
240    /// Copies bytes from a pointer into a new AzString.
241    /// This is useful for C FFI where you have a char* buffer.
242    #[inline]
243    pub fn copy_from_bytes(ptr: *const u8, start: usize, len: usize) -> Self {
244        Self {
245            vec: U8Vec::copy_from_bytes(ptr, start, len),
246        }
247    }
248
249    #[inline]
250    pub fn from_string(s: String) -> Self {
251        Self {
252            vec: U8Vec::from_vec(s.into_bytes()),
253        }
254    }
255
256    #[inline]
257    pub fn as_str(&self) -> &str {
258        unsafe { core::str::from_utf8_unchecked(self.vec.as_ref()) }
259    }
260
261    /// NOTE: CLONES the memory if the memory is external or &'static
262    /// Moves the memory out if the memory is library-allocated
263    #[inline]
264    pub fn clone_self(&self) -> Self {
265        Self {
266            vec: self.vec.clone_self(),
267        }
268    }
269
270    #[inline]
271    pub fn into_library_owned_string(self) -> String {
272        match self.vec.destructor {
273            U8VecDestructor::NoDestructor | U8VecDestructor::External(_) => {
274                self.as_str().to_string()
275            }
276            U8VecDestructor::DefaultRust => {
277                let m = core::mem::ManuallyDrop::new(self);
278                unsafe { String::from_raw_parts(m.vec.ptr as *mut u8, m.vec.len, m.vec.cap) }
279            }
280        }
281    }
282
283    #[inline]
284    pub fn as_bytes(&self) -> &[u8] {
285        self.vec.as_ref()
286    }
287
288    #[inline]
289    pub fn into_bytes(self) -> U8Vec {
290        let m = core::mem::ManuallyDrop::new(self);
291        U8Vec {
292            ptr: m.vec.ptr,
293            len: m.vec.len,
294            cap: m.vec.cap,
295            destructor: m.vec.destructor,
296            run_destructor: m.vec.run_destructor,
297        }
298    }
299
300    /// Returns the length of the string in bytes (not including null terminator)
301    #[inline]
302    pub fn len(&self) -> usize {
303        self.vec.len
304    }
305
306    /// Returns true if the string is empty
307    #[inline]
308    pub fn is_empty(&self) -> bool {
309        self.vec.len == 0
310    }
311
312    /// Creates a null-terminated copy of the string for C FFI usage.
313    /// Returns a new U8Vec that contains the string data followed by a null byte.
314    /// The caller is responsible for freeing this memory.
315    ///
316    /// Use this when you need to pass a string to C code that expects `const char*`.
317    #[inline]
318    pub fn to_c_str(&self) -> U8Vec {
319        let bytes = self.as_bytes();
320        let mut result = Vec::with_capacity(bytes.len() + 1);
321        result.extend_from_slice(bytes);
322        result.push(0); // null terminator
323        U8Vec::from_vec(result)
324    }
325
326    /// Creates a new AzString from UTF-16 encoded bytes (little-endian).
327    /// Returns an empty string if the input is invalid UTF-16 or has odd length.
328    ///
329    /// # Arguments
330    /// * `ptr` - Pointer to UTF-16 encoded bytes
331    /// * `len` - Length in bytes (not code units) - must be even
332    ///
333    /// # Safety
334    /// - `ptr` must be valid for reading `len` bytes
335    /// - `len` must be even (UTF-16 uses 2 bytes per code unit)
336    #[inline]
337    pub unsafe fn from_utf16_le(ptr: *const u8, len: usize) -> Self {
338        if ptr.is_null() || len == 0 {
339            return Self::default();
340        }
341        
342        // UTF-16 requires pairs of bytes
343        if len % 2 != 0 {
344            return Self::default();
345        }
346        
347        let byte_slice = core::slice::from_raw_parts(ptr, len);
348        let code_units: Vec<u16> = byte_slice
349            .chunks_exact(2)
350            .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
351            .collect();
352        
353        match String::from_utf16(&code_units) {
354            Ok(s) => Self::from_string(s),
355            Err(_) => Self::default(),
356        }
357    }
358
359    /// Creates a new AzString from UTF-16 encoded bytes (big-endian).
360    /// Returns an empty string if the input is invalid UTF-16 or has odd length.
361    ///
362    /// # Arguments
363    /// * `ptr` - Pointer to UTF-16 encoded bytes
364    /// * `len` - Length in bytes (not code units) - must be even
365    ///
366    /// # Safety
367    /// - `ptr` must be valid for reading `len` bytes
368    /// - `len` must be even (UTF-16 uses 2 bytes per code unit)
369    #[inline]
370    pub unsafe fn from_utf16_be(ptr: *const u8, len: usize) -> Self {
371        if ptr.is_null() || len == 0 {
372            return Self::default();
373        }
374        
375        // UTF-16 requires pairs of bytes
376        if len % 2 != 0 {
377            return Self::default();
378        }
379        
380        let byte_slice = core::slice::from_raw_parts(ptr, len);
381        let code_units: Vec<u16> = byte_slice
382            .chunks_exact(2)
383            .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
384            .collect();
385        
386        match String::from_utf16(&code_units) {
387            Ok(s) => Self::from_string(s),
388            Err(_) => Self::default(),
389        }
390    }
391
392    /// Creates a new AzString from UTF-8 bytes with lossy conversion.
393    /// Invalid UTF-8 sequences are replaced with the Unicode replacement character (U+FFFD).
394    ///
395    /// # Safety
396    /// - `ptr` must be valid for reading `len` bytes
397    #[inline]
398    pub unsafe fn from_utf8_lossy(ptr: *const u8, len: usize) -> Self {
399        if ptr.is_null() || len == 0 {
400            return Self::default();
401        }
402        
403        let byte_slice = core::slice::from_raw_parts(ptr, len);
404        let s = String::from_utf8_lossy(byte_slice).into_owned();
405        Self::from_string(s)
406    }
407
408    /// Creates a new AzString from UTF-8 bytes.
409    /// Returns an empty string if the input is not valid UTF-8.
410    ///
411    /// # Safety
412    /// - `ptr` must be valid for reading `len` bytes
413    #[inline]
414    pub unsafe fn from_utf8(ptr: *const u8, len: usize) -> Self {
415        if ptr.is_null() || len == 0 {
416            return Self::default();
417        }
418        
419        let byte_slice = core::slice::from_raw_parts(ptr, len);
420        match core::str::from_utf8(byte_slice) {
421            Ok(s) => Self::from_string(s.to_string()),
422            Err(_) => Self::default(),
423        }
424    }
425}
426
427impl From<String> for AzString {
428    fn from(input: String) -> AzString {
429        AzString::from_string(input)
430    }
431}
432
433impl PartialOrd for AzString {
434    fn partial_cmp(&self, rhs: &Self) -> Option<core::cmp::Ordering> {
435        self.as_str().partial_cmp(rhs.as_str())
436    }
437}
438
439impl Ord for AzString {
440    fn cmp(&self, rhs: &Self) -> core::cmp::Ordering {
441        self.as_str().cmp(rhs.as_str())
442    }
443}
444
445impl Clone for AzString {
446    fn clone(&self) -> Self {
447        self.clone_self()
448    }
449}
450
451impl PartialEq for AzString {
452    fn eq(&self, rhs: &Self) -> bool {
453        self.as_str().eq(rhs.as_str())
454    }
455}
456
457impl Eq for AzString {}
458
459impl core::hash::Hash for AzString {
460    fn hash<H>(&self, state: &mut H)
461    where
462        H: core::hash::Hasher,
463    {
464        self.as_str().hash(state)
465    }
466}
467
468impl core::ops::Deref for AzString {
469    type Target = str;
470
471    fn deref(&self) -> &str {
472        self.as_str()
473    }
474}
475
476impl_option!(
477    u8,
478    OptionU8,
479    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
480);
481
482impl_vec!(u8, U8Vec, U8VecDestructor, U8VecDestructorType, U8VecSlice, OptionU8);
483impl_vec_mut!(u8, U8Vec);
484impl_vec_debug!(u8, U8Vec);
485impl_vec_partialord!(u8, U8Vec);
486impl_vec_ord!(u8, U8Vec);
487impl_vec_clone!(u8, U8Vec, U8VecDestructor);
488impl_vec_partialeq!(u8, U8Vec);
489impl_vec_eq!(u8, U8Vec);
490impl_vec_hash!(u8, U8Vec);
491
492impl U8Vec {
493    /// Copies bytes from a pointer into a new Vec.
494    /// This is useful for C FFI where you have a uint8_t* buffer.
495    #[inline]
496    pub fn copy_from_bytes(ptr: *const u8, start: usize, len: usize) -> Self {
497        if ptr.is_null() || len == 0 {
498            return Self::new();
499        }
500        let slice = unsafe { core::slice::from_raw_parts(ptr.add(start), len) };
501        Self::from_vec(slice.to_vec())
502    }
503}
504
505impl_option!(
506    U8Vec,
507    OptionU8Vec,
508    copy = false,
509    [Debug, Clone, PartialEq, Ord, PartialOrd, Eq, Hash]
510);
511
512impl_vec!(u16, U16Vec, U16VecDestructor, U16VecDestructorType, U16VecSlice, OptionU16);
513impl_vec_debug!(u16, U16Vec);
514impl_vec_partialord!(u16, U16Vec);
515impl_vec_ord!(u16, U16Vec);
516impl_vec_clone!(u16, U16Vec, U16VecDestructor);
517impl_vec_partialeq!(u16, U16Vec);
518impl_vec_eq!(u16, U16Vec);
519impl_vec_hash!(u16, U16Vec);
520
521impl_vec!(f32, F32Vec, F32VecDestructor, F32VecDestructorType, F32VecSlice, OptionF32);
522impl_vec_debug!(f32, F32Vec);
523impl_vec_partialord!(f32, F32Vec);
524impl_vec_clone!(f32, F32Vec, F32VecDestructor);
525impl_vec_partialeq!(f32, F32Vec);
526
527// Vec<char>
528impl_vec!(u32, U32Vec, U32VecDestructor, U32VecDestructorType, U32VecSlice, OptionU32);
529impl_vec_mut!(u32, U32Vec);
530impl_vec_debug!(u32, U32Vec);
531impl_vec_partialord!(u32, U32Vec);
532impl_vec_ord!(u32, U32Vec);
533impl_vec_clone!(u32, U32Vec, U32VecDestructor);
534impl_vec_partialeq!(u32, U32Vec);
535impl_vec_eq!(u32, U32Vec);
536impl_vec_hash!(u32, U32Vec);
537
538impl_vec!(AzString, StringVec, StringVecDestructor, StringVecDestructorType, StringVecSlice, OptionString);
539impl_vec_debug!(AzString, StringVec);
540impl_vec_partialord!(AzString, StringVec);
541impl_vec_ord!(AzString, StringVec);
542impl_vec_clone!(AzString, StringVec, StringVecDestructor);
543impl_vec_partialeq!(AzString, StringVec);
544impl_vec_eq!(AzString, StringVec);
545impl_vec_hash!(AzString, StringVec);
546
547impl From<Vec<String>> for StringVec {
548    fn from(v: Vec<String>) -> StringVec {
549        let new_v: Vec<AzString> = v.into_iter().map(|s| s.into()).collect();
550        new_v.into()
551    }
552}
553
554impl_option!(
555    StringVec,
556    OptionStringVec,
557    copy = false,
558    [Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash]
559);
560
561impl_option!(
562    u16,
563    OptionU16,
564    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
565);
566impl_option!(
567    u32,
568    OptionU32,
569    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
570);
571impl_option!(
572    u64,
573    OptionU64,
574    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
575);
576impl_option!(
577    usize,
578    OptionUsize,
579    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
580);
581impl_option!(
582    i16,
583    OptionI16,
584    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
585);
586impl_option!(
587    i32,
588    OptionI32,
589    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
590);
591impl_option!(bool, OptionBool, [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]);
592impl_option!(f32, OptionF32, [Debug, Copy, Clone, PartialEq, PartialOrd]);
593impl_option!(f64, OptionF64, [Debug, Copy, Clone, PartialEq, PartialOrd]);
594
595// Manual implementations for Hash and Ord on OptionF32 (since f32 doesn't implement these traits)
596impl core::hash::Hash for OptionF32 {
597    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
598        match self {
599            OptionF32::None => 0u8.hash(state),
600            OptionF32::Some(v) => {
601                1u8.hash(state);
602                v.to_bits().hash(state);
603            }
604        }
605    }
606}
607
608impl Eq for OptionF32 {}
609
610impl Ord for OptionF32 {
611    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
612        match (self, other) {
613            (OptionF32::None, OptionF32::None) => core::cmp::Ordering::Equal,
614            (OptionF32::None, OptionF32::Some(_)) => core::cmp::Ordering::Less,
615            (OptionF32::Some(_), OptionF32::None) => core::cmp::Ordering::Greater,
616            (OptionF32::Some(a), OptionF32::Some(b)) => {
617                a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal)
618            }
619        }
620    }
621}