devela/text/grapheme/
string_u8.rs

1// devela::text::grapheme::string_u8
2//
3//!
4//
5// TOC
6// - definitions
7// - trait impls
8
9use super::Grapheme;
10#[cfg(_char··)]
11use crate::text::char::*;
12#[cfg(feature = "alloc")]
13use crate::CString;
14use crate::{unwrap, ConstDefault, IterChars, MismatchedCapacity, StringU8};
15// use unicode_segmentation::UnicodeSegmentation;
16
17/* definitions */
18
19/// An <abbr title="Extended Grapheme Cluster">EGC</abbr> backed by an
20/// [`StringU8`].
21#[derive(Clone, PartialEq, Eq, Hash)]
22#[repr(transparent)]
23#[must_use]
24pub struct GraphemeU8<const CAP: usize>(StringU8<CAP>);
25
26impl<const CAP: usize> GraphemeU8<CAP> {
27    /// Creates a new empty `GraphemeU8`.
28    ///
29    /// # Errors
30    /// Returns [`MismatchedCapacity`] if `CAP > 255.
31    pub const fn new() -> Result<Self, MismatchedCapacity> {
32        Ok(Self(unwrap![ok? StringU8::new()]))
33    }
34
35    /// Creates a new `GraphemeU8` from a `char7`.
36    ///
37    /// # Errors
38    /// Returns [`MismatchedCapacity`] if `CAP > 255.
39    ///
40    /// Will always succeed if `CAP` >= 1 and <= 255.
41    #[cfg(feature = "_char7")]
42    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "_char7")))]
43    pub const fn from_char7(c: char7) -> Result<Self, MismatchedCapacity> {
44        Ok(Self(unwrap![ok? StringU8::from_char7(c)]))
45    }
46
47    /// Creates a new `GraphemeU8` from a `char8`.
48    ///
49    /// # Errors
50    /// Returns [`MismatchedCapacity`] if `CAP` > 255
51    /// or < `c.`[`len_utf8()`][char8#method.len_utf8].
52    ///
53    /// Will always succeed if `CAP` >= 2 and <= 255.
54    #[cfg(feature = "_char8")]
55    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "_char8")))]
56    pub const fn from_char8(c: char8) -> Result<Self, MismatchedCapacity> {
57        Ok(Self(unwrap![ok? StringU8::from_char8(c)]))
58    }
59
60    /// Creates a new `GraphemeU8` from a `char16`.
61    ///
62    /// # Errors
63    /// Returns [`MismatchedCapacity`] if `CAP` > 255
64    /// or < `c.`[`len_utf8()`][char16#method.len_utf8].
65    ///
66    /// Will always succeed if `CAP` >= 3 and <= 255.
67    #[cfg(feature = "_char16")]
68    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "_char16")))]
69    pub const fn from_char16(c: char16) -> Result<Self, MismatchedCapacity> {
70        Ok(Self(unwrap![ok? StringU8::from_char16(c)]))
71    }
72
73    /// Creates a new `GraphemeU8` from a `char`.
74    /// # Errors
75    /// Returns [`MismatchedCapacity`] if `CAP` > 255
76    /// or < `c.`[`len_utf8()`][UnicodeScalar#method.len_utf8].
77    ///
78    /// Will never panic if `CAP` >= 4 and <= 255.
79    pub const fn from_char(c: char) -> Result<Self, MismatchedCapacity> {
80        Ok(Self(unwrap![ok? StringU8::from_char(c)]))
81    }
82
83    //
84
85    /// Returns the length in bytes.
86    #[must_use] #[rustfmt::skip]
87    pub const fn len(&self) -> usize { self.0.len() }
88
89    /// Returns `true` if the current length is 0.
90    #[must_use] #[rustfmt::skip]
91    pub const fn is_empty(&self) -> bool { self.0.len() == 0 }
92
93    /// Returns the total capacity in bytes.
94    #[must_use] #[rustfmt::skip]
95    pub const fn capacity() -> usize { CAP }
96
97    /// Returns the remaining capacity.
98    #[must_use] #[rustfmt::skip]
99    pub const fn remaining_capacity(&self) -> usize { CAP - self.len() }
100
101    /// Returns `true` if the current remaining capacity is 0.
102    #[must_use] #[rustfmt::skip]
103    pub const fn is_full(&self) -> bool { self.len() == CAP }
104
105    /// Sets the length to 0, by resetting all bytes to 0.
106    #[rustfmt::skip]
107    pub fn clear(&mut self) { self.0.clear(); }
108
109    //
110
111    /// Returns a byte slice of the inner string slice.
112    #[must_use] #[rustfmt::skip]
113    pub const fn as_bytes(&self) -> &[u8] { self.0.as_bytes() }
114
115    /// Returns a mutable byte slice of the inner string slice.
116    /// # Safety
117    /// The content must be valid UTF-8.
118    #[must_use]
119    #[cfg(all(not(feature = "safe_text"), feature = "unsafe_slice"))]
120    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "unsafe_slice")))]
121    pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
122        // SAFETY: caller must ensure safety
123        unsafe { self.0.as_bytes_mut() }
124    }
125
126    /// Returns a copy of the inner array with the full contents.
127    ///
128    /// The array contains all the bytes, including those outside the current length.
129    #[must_use] #[rustfmt::skip]
130    pub const fn as_array(&self) -> [u8; CAP] { self.0.as_array() }
131
132    /// Returns the inner array with the full contents.
133    ///
134    /// The array contains all the bytes, including those outside the current length.
135    #[must_use] #[rustfmt::skip]
136    pub const fn into_array(self) -> [u8; CAP] { self.0.into_array() }
137
138    /// Returns the inner string slice.
139    #[must_use] #[rustfmt::skip]
140    pub const fn as_str(&self) -> &str { self.0.as_str() }
141
142    /// Returns the mutable inner string slice.
143    /// # Safety
144    /// The content must be valid UTF-8.
145    #[cfg(all(not(feature = "safe_text"), feature = "unsafe_slice"))]
146    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "unsafe_slice")))]
147    pub unsafe fn as_mut_str(&mut self) -> &mut str {
148        self.0.as_mut_str()
149    }
150
151    /// Returns an iterator over the `chars` of this grapheme cluster.
152    #[rustfmt::skip]
153    pub fn chars(&self) -> IterChars { self.0.chars() }
154
155    /// Returns a new allocated C-compatible, nul-terminanted string.
156    #[rustfmt::skip]
157    #[cfg(feature = "alloc")]
158    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "alloc")))]
159    pub fn to_cstring(&self) -> CString { self.0.to_cstring() }
160}
161
162/* traits */
163
164impl<const CAP: usize> Grapheme for GraphemeU8<CAP> {}
165
166mod core_impls {
167    use super::*;
168    use core::fmt;
169
170    impl<const CAP: usize> Default for GraphemeU8<CAP> {
171        /// Returns an empty extended grapheme character.
172        ///
173        /// # Panics
174        /// Panics if `CAP` > 255.
175        #[rustfmt::skip]
176        fn default() -> Self { unwrap![ok Self::new()] }
177    }
178    impl<const CAP: usize> ConstDefault for GraphemeU8<CAP> {
179        /// Returns an empty string.
180        ///
181        /// # Panics
182        /// Panics if `CAP > 255`.
183        const DEFAULT: Self = unwrap![ok Self::new()];
184    }
185
186    impl<const CAP: usize> fmt::Display for GraphemeU8<CAP> {
187        #[rustfmt::skip]
188        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }
189    }
190    impl<const CAP: usize> fmt::Debug for GraphemeU8<CAP> {
191        #[rustfmt::skip]
192        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.0) }
193    }
194
195    // impl From<String> for GraphemeU8 {
196    //     fn from(s: String) -> GraphemeU8 {
197    //         GraphemeU8(s.graphemes(true).take(1).collect())
198    //     }
199    // }
200    // impl From<&str> for GraphemeU8 {
201    //     fn from(s: &str) -> GraphemeU8 {
202    //         GraphemeU8(s.graphemes(true).take(1).collect())
203    //     }
204    // }
205    // impl From<char> for GraphemeU8 {
206    //     fn from(s: char) -> GraphemeU8 {
207    //         GraphemeU8(s.into())
208    //     }
209    // }
210}