@@ -2,6 +2,7 @@ use core::fmt;
22
33use crate :: helper:: { compare_encodings, Helper , NestingLevel } ;
44use crate :: parse:: Parser ;
5+ use crate :: static_str:: { static_encoding_str_array, static_encoding_str_len} ;
56use crate :: EncodingBox ;
67
78/// An Objective-C type-encoding.
@@ -281,6 +282,81 @@ impl Encoding {
281282 pub fn size ( & self ) -> Option < usize > {
282283 Helper :: new ( self ) . size ( NestingLevel :: new ( ) )
283284 }
285+
286+ /// The length of the encoding when turned into a string.
287+ ///
288+ /// This returns the same value as `self.to_string().len()`, but is
289+ /// available in `const` to allow constructing static encoding strings.
290+ ///
291+ /// See [`str_array`][Self::str_array] for more information.
292+ pub const fn str_len ( & self ) -> usize {
293+ static_encoding_str_len ( self , NestingLevel :: new ( ) )
294+ }
295+
296+ /// An array containing the encoding's contents.
297+ ///
298+ /// The length should be at least [`str_len`][Self::str_len], remaining
299+ /// bytes are zero-filled.
300+ ///
301+ /// This is equivalent to `self.to_string().to_bytes()`, but is available
302+ /// in `const` to allow constructing static encoding strings.
303+ ///
304+ /// This is most often useful in macros - it is not really possible to use
305+ /// in generics yet, it requires [`#![feature(generic_const_exprs)]`][g],
306+ /// but once that's available, this should be useful there too.
307+ ///
308+ /// [g]: https://github.com/rust-lang/rust/issues/76560
309+ ///
310+ /// # Panics
311+ ///
312+ /// Panics if `LEN` is less than [`str_len`][Self::str_len], or if the
313+ /// encoding contains structs or unions with invalid identifiers.
314+ ///
315+ /// # Example
316+ ///
317+ /// Construct an encoding string at `const`-time.
318+ ///
319+ /// ```
320+ /// use core::ffi::CStr;
321+ /// use objc2_encode::Encoding;
322+ ///
323+ /// // Let's say you have some constant encoding.
324+ /// const ENCODING: Encoding = Encoding::Pointer(&Encoding::Struct("foo", &[Encoding::Sel, Encoding::Int]));
325+ ///
326+ /// // This can be converted to a constant string.
327+ /// //
328+ /// // SAFETY: `.str_array()` is guaranteed to be valid UTF-8.
329+ /// const ENCODING_STR: &str = unsafe {
330+ /// str::from_utf8_unchecked(&ENCODING.str_array::<{ ENCODING.str_len() }>())
331+ /// };
332+ /// assert_eq!(ENCODING_STR, "^{foo=:i}");
333+ ///
334+ /// // Or to a constant C-string.
335+ /// //
336+ /// // SAFETY: `.str_array()` is guaranteed to not contain any NUL bytes,
337+ /// // apart from at the end because we specify `.str_len() + 1`.
338+ /// const ENCODING_CSTR: &CStr = unsafe {
339+ /// CStr::from_bytes_with_nul_unchecked(&ENCODING.str_array::<{ ENCODING.str_len() + 1 }>())
340+ /// };
341+ /// assert_eq!(ENCODING_CSTR, c"^{foo=:i}");
342+ /// ```
343+ ///
344+ /// Attempt to construct an encoding string from an invalid encoding.
345+ ///
346+ /// ```should_panic
347+ /// use objc2_encode::Encoding;
348+ ///
349+ /// // Invalid struct name (`-` is not an identifier).
350+ /// const ENCODING: Encoding = Encoding::Pointer(&Encoding::Struct("-", &[Encoding::Int]));
351+ ///
352+ /// // This will panic.
353+ /// let _ = str::from_utf8(&ENCODING.str_array::<{ ENCODING.str_len() }>()).unwrap();
354+ ///
355+ /// // If we did ^ at `const`-time, it would be a post-mono compile-error.
356+ /// ```
357+ pub const fn str_array < const LEN : usize > ( & self ) -> [ u8 ; LEN ] {
358+ static_encoding_str_array ( self , NestingLevel :: new ( ) )
359+ }
284360}
285361
286362/// Formats this [`Encoding`] in a similar way that the `@encode` directive
@@ -298,7 +374,6 @@ impl fmt::Display for Encoding {
298374#[ cfg( test) ]
299375mod tests {
300376 use super :: * ;
301- use crate :: static_str:: { static_encoding_str_array, static_encoding_str_len} ;
302377 use alloc:: boxed:: Box ;
303378 use alloc:: string:: ToString ;
304379 use alloc:: vec;
@@ -387,7 +462,7 @@ mod tests {
387462 ) *
388463
389464 // Check static str
390- const STATIC_ENCODING_DATA : [ u8 ; static_encoding_str_len ( & E , NestingLevel :: new ( ) ) ] = static_encoding_str_array ( & E , NestingLevel :: new ( ) ) ;
465+ const STATIC_ENCODING_DATA : [ u8 ; E . str_len ( ) ] = E . str_array ( ) ;
391466 const STATIC_ENCODING_STR : & str = unsafe { core:: str :: from_utf8_unchecked( & STATIC_ENCODING_DATA ) } ;
392467 assert_eq!( STATIC_ENCODING_STR , $string, "static" ) ;
393468 }
0 commit comments