Skip to content

Commit 5c61473

Browse files
authored
Merge pull request #70 from madsmtm/static-encoding-str
Generate static method encodings inside `define_class!`.
2 parents 221d710 + ace5af3 commit 5c61473

File tree

28 files changed

+697
-441
lines changed

28 files changed

+697
-441
lines changed

crates/objc2-encode/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88

99
### Added
1010
* Added `Encoding::Int128` and `Encoding::UInt128`.
11+
* Added `Encoding::str_len` and `Encoding::str_array` to allow statically
12+
constructing encoding strings.
1113

1214
### Changed
1315
* Equivalence comparisons now consider `Encoding::String` as equivalent to

crates/objc2-encode/src/encoding.rs

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use core::fmt;
22

33
use crate::helper::{compare_encodings, Helper, NestingLevel};
44
use crate::parse::Parser;
5+
use crate::static_str::{static_encoding_str_array, static_encoding_str_len};
56
use 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)]
299375
mod 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
}

crates/objc2-encode/src/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@ mod encoding;
5454
mod encoding_box;
5555
mod helper;
5656
mod parse;
57-
58-
// Will be used outside tests at some point when generic constants are available
59-
#[cfg(test)]
6057
mod static_str;
6158

6259
pub use self::encoding::Encoding;

0 commit comments

Comments
 (0)