diff --git a/Cargo.toml b/Cargo.toml index 99013c4..b27d8f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "objc-encode" -version = "1.1.0" +version = "1.1.0" # Remember to update html_root_url in lib.rs authors = ["Steven Sheldon"] edition = "2018" @@ -9,7 +9,7 @@ keywords = ["objective-c", "osx", "ios", "cocoa", "uikit"] categories = ["development-tools::ffi", "no-std"] readme = "README.md" repository = "http://github.com/SSheldon/rust-objc-encode" -documentation = "http://ssheldon.github.io/rust-objc/objc_encode/" +documentation = "https://docs.rs/objc-encode/" license = "MIT" exclude = [ diff --git a/README.md b/README.md index 38ecd2b..e311b5c 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,21 @@ +# ``objc-encode`` + +[![Crates.io](https://img.shields.io/crates/v/objc-encode.svg)](https://crates.io/crates/objc-encode) +[![Docs.rs](https://docs.rs/objc-encode/badge.svg)](https://docs.rs/objc-encode/) + Objective-C type encoding creation and parsing in Rust. The Objective-C compiler encodes types as strings for usage in the runtime. This crate aims to provide a strongly-typed (rather than stringly-typed) way to create and describe these type encodings without memory allocation in Rust. -# Implementing Encode + +## Implementing Encode This crate declares an `Encode` trait that can be implemented for types that the Objective-C compiler can encode. Implementing this trait looks like: -``` rust +```rust unsafe impl Encode for CGPoint { const ENCODING: Encoding<'static> = Encoding::Struct("CGPoint", &[CGFloat::ENCODING, CGFLOAT::ENCODING]); @@ -19,20 +25,20 @@ unsafe impl Encode for CGPoint { For an example of how this works with more complex types, like structs containing structs, see the `core_graphics` example. -# Comparing with encoding strings +## Comparing with encoding strings An `Encoding` can be compared with an encoding string from the Objective-C runtime: -``` rust +```rust assert!(&i32::ENCODING == "i"); ``` -# Generating encoding strings +## Generating encoding strings Every `Encoding` implements `Display` as its string representation. This can be generated conveniently through the `to_string` method: -``` rust +```rust assert_eq!(i32::ENCODING.to_string(), "i"); ``` diff --git a/examples/ns_string.rs b/examples/ns_string.rs new file mode 100644 index 0000000..85f51a8 --- /dev/null +++ b/examples/ns_string.rs @@ -0,0 +1,34 @@ +use objc_encode::{Encode, Encoding}; + +/// We don't know the size of NSString, so we can only hold pointers to it. +/// +/// TODO: Use [`extern type`][rfc-1861] when that gets stabilized. +/// +/// [rfc-1861]: https://rust-lang.github.io/rfcs/1861-extern-types.html +#[repr(C)] +struct NSString { + _priv: [u8; 0], +} + +/// Implement `Encode` for references. +/// +/// This also implements for `*mut NSString` and `Option<&mut NSString>`. +unsafe impl<'a> Encode for &'a NSString { + const ENCODING: Encoding<'static> = Encoding::Object; +} + +/// Implement `Encode` for mutable references. +/// +/// This also implements for `*mut NSString` and `Option<&mut NSString>`. +unsafe impl<'a> Encode for &'a mut NSString { + const ENCODING: Encoding<'static> = Encoding::Object; +} + +fn main() { + println!("{}", <*const NSString>::ENCODING); + println!("{}", <*mut NSString>::ENCODING); + println!("{}", <&NSString>::ENCODING); + println!("{}", <&mut NSString>::ENCODING); + println!("{}", Option::<&NSString>::ENCODING); + println!("{}", Option::<&mut NSString>::ENCODING); +} diff --git a/src/encode.rs b/src/encode.rs index cd59210..f9bb552 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -4,11 +4,13 @@ use crate::Encoding; /// Types that have an Objective-C type encoding. /// -/// Unsafe because Objective-C will make assumptions about the type (like its -/// size and alignment) from its encoding, so the implementer must verify that -/// the encoding is accurate. +/// # Safety +/// +/// Objective-C will make assumptions about the type (like its size and +/// alignment) from its encoding, so the implementer must verify that the +/// encoding is accurate. pub unsafe trait Encode { - /// Returns the Objective-C type encoding for Self. + /// The Objective-C type encoding for `Self`. const ENCODING: Encoding<'static>; } @@ -86,18 +88,30 @@ External crates cannot implement Encode for pointers or Optionals, but they As a workaround, we provide implementations for these types that return the same encoding as references. */ -unsafe impl Encode for *const T where for<'b> &'b T: Encode { +unsafe impl Encode for *const T +where + for<'b> &'b T: Encode, +{ const ENCODING: Encoding<'static> = <&T>::ENCODING; } -unsafe impl Encode for *mut T where for<'b> &'b mut T: Encode { +unsafe impl Encode for *mut T +where + for<'b> &'b mut T: Encode, +{ const ENCODING: Encoding<'static> = <&mut T>::ENCODING; } -unsafe impl<'a, T> Encode for Option<&'a T> where for<'b> &'b T: Encode { +unsafe impl<'a, T> Encode for Option<&'a T> +where + for<'b> &'b T: Encode, +{ const ENCODING: Encoding<'static> = <&T>::ENCODING; } -unsafe impl<'a, T> Encode for Option<&'a mut T> where for<'b> &'b mut T: Encode { +unsafe impl<'a, T> Encode for Option<&'a mut T> +where + for<'b> &'b mut T: Encode, +{ const ENCODING: Encoding<'static> = <&mut T>::ENCODING; } diff --git a/src/encoding.rs b/src/encoding.rs index 099ee8d..fdb8e1a 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -8,30 +8,65 @@ use crate::parse; /// #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Encoding<'a> { + /// A C `char`. Corresponds to the `c` code. Char, + /// A C `short`. Corresponds to the `s` code. Short, + /// A C `int`. Corresponds to the `i` code. Int, + /// A C `long`. Corresponds to the `l` code. Long, + /// A C `long long`. Corresponds to the `q` code. LongLong, + /// A C `unsigned char`. Corresponds to the `C` code. UChar, + /// A C `unsigned short`. Corresponds to the `S` code. UShort, + /// A C `unsigned int`. Corresponds to the `I` code. UInt, + /// A C `unsigned long`. Corresponds to the `L` code. ULong, + /// A C `unsigned long long`. Corresponds to the `Q` code. ULongLong, + /// A C `float`. Corresponds to the `f` code. Float, + /// A C `double`. Corresponds to the `d` code. Double, + /// A C++ `bool` / C99 `_Bool`. Corresponds to the `B` code. Bool, + /// A C `void`. Corresponds to the `v` code. Void, + /// A C `char *`. Corresponds to the `*` code. String, + /// An Objective-C object (`id`). Corresponds to the `@` code. Object, + /// An Objective-C block. Corresponds to the `@?` code. Block, + /// An Objective-C class (`Class`). Corresponds to the `#` code. Class, + /// An Objective-C selector (`SEL`). Corresponds to the `:` code. Sel, + /// An unknown type. Corresponds to the `?` code. Unknown, + /// A bitfield with the given number of bits. + /// + /// Corresponds to the `b`num code. BitField(u32), + /// A pointer to the given type. + /// + /// Corresponds to the `^`type code. Pointer(&'a Encoding<'a>), + /// An array with the given length and type. + /// + /// Corresponds to the `[len type]` code. Array(u32, &'a Encoding<'a>), + /// A struct with the given name and fields. + /// + /// Corresponds to the `{name=fields...}` code. Struct(&'a str, &'a [Encoding<'a>]), + /// A union with the given name and fields. + /// + /// Corresponds to the `(name=fields...)` code. Union(&'a str, &'a [Encoding<'a>]), } @@ -39,26 +74,26 @@ impl fmt::Display for Encoding<'_> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { use Encoding::*; let code = match *self { - Char => "c", - Short => "s", - Int => "i", - Long => "l", - LongLong => "q", - UChar => "C", - UShort => "S", - UInt => "I", - ULong => "L", + Char => "c", + Short => "s", + Int => "i", + Long => "l", + LongLong => "q", + UChar => "C", + UShort => "S", + UInt => "I", + ULong => "L", ULongLong => "Q", - Float => "f", - Double => "d", - Bool => "B", - Void => "v", - String => "*", - Object => "@", - Block => "@?", - Class => "#", - Sel => ":", - Unknown => "?", + Float => "f", + Double => "d", + Bool => "B", + Void => "v", + String => "*", + Object => "@", + Block => "@?", + Class => "#", + Sel => ":", + Unknown => "?", BitField(b) => { return write!(formatter, "b{}", b); } @@ -101,8 +136,8 @@ impl PartialEq> for str { #[cfg(test)] mod tests { - use std::string::ToString; use super::Encoding; + use alloc::string::ToString; #[test] fn test_array_display() { diff --git a/src/lib.rs b/src/lib.rs index 12052da..d1f631e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,10 +7,10 @@ to create and describe these type encodings without memory allocation in Rust. # Implementing Encode -This crate declares an `Encode` trait that can be implemented for types that +This crate declares an [`Encode`] trait that can be implemented for types that the Objective-C compiler can encode. Implementing this trait looks like: -``` ignore +```ignore unsafe impl Encode for CGPoint { const ENCODING: Encoding<'static> = Encoding::Struct("CGPoint", &[CGFloat::ENCODING, CGFLOAT::ENCODING]); @@ -22,7 +22,7 @@ containing structs, see the `core_graphics` example. # Comparing with encoding strings -An `Encoding` can be compared with an encoding string from the Objective-C +An [`Encoding`] can be compared with an encoding string from the Objective-C runtime: ``` @@ -32,8 +32,9 @@ assert!(&i32::ENCODING == "i"); # Generating encoding strings -Every `Encoding` implements `Display` as its string representation. -This can be generated conveniently through the `to_string` method: +Every [`Encoding`] implements [`Display`][`core::fmt::Display`] as its string +representation. This can be generated conveniently through the +[`to_string`][`alloc::string::ToString::to_string`] method: ``` # use objc_encode::Encode; @@ -42,13 +43,16 @@ assert_eq!(i32::ENCODING.to_string(), "i"); */ #![no_std] +#![warn(missing_docs)] +// Update in Cargo.toml as well. +#![doc(html_root_url = "https://docs.rs/objc-encode/1.1.0")] -#[cfg(test)] -extern crate std; +#[cfg(any(test, doc))] +extern crate alloc; -mod encoding; mod encode; +mod encoding; mod parse; -pub use crate::encoding::Encoding; pub use crate::encode::Encode; +pub use crate::encoding::Encoding; diff --git a/src/parse.rs b/src/parse.rs index 645776f..e935043 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -15,26 +15,26 @@ const QUALIFIERS: &'static [char] = &[ fn rm_enc_prefix<'a>(s: &'a str, enc: &Encoding) -> Option<&'a str> { use Encoding::*; let code = match *enc { - Char => "c", - Short => "s", - Int => "i", - Long => "l", - LongLong => "q", - UChar => "C", - UShort => "S", - UInt => "I", - ULong => "L", + Char => "c", + Short => "s", + Int => "i", + Long => "l", + LongLong => "q", + UChar => "C", + UShort => "S", + UInt => "I", + ULong => "L", ULongLong => "Q", - Float => "f", - Double => "d", - Bool => "B", - Void => "v", - String => "*", - Object => "@", - Block => "@?", - Class => "#", - Sel => ":", - Unknown => "?", + Float => "f", + Double => "d", + Bool => "B", + Void => "v", + String => "*", + Object => "@", + Block => "@?", + Class => "#", + Sel => ":", + Unknown => "?", BitField(b) => { let s = rm_prefix(s, "b")?; return rm_int_prefix(s, b); @@ -85,8 +85,7 @@ fn chomp_int(s: &str) -> Option<(u32, &str)> { } fn rm_int_prefix(s: &str, other: u32) -> Option<&str> { - chomp_int(s) - .and_then(|(n, t)| if other == n { Some(t) } else { None }) + chomp_int(s).and_then(|(n, t)| if other == n { Some(t) } else { None }) } fn rm_prefix<'a>(s: &'a str, other: &str) -> Option<&'a str> { @@ -112,17 +111,16 @@ mod tests { #[test] fn test_nested() { - let enc = Encoding::Struct("A", &[ - Encoding::Struct("B", &[ + let enc = Encoding::Struct( + "A", + &[ + Encoding::Struct("B", &[Encoding::Char, Encoding::Int]), Encoding::Char, Encoding::Int, - ]), - Encoding::Char, - Encoding::Int, - ]); + ], + ); assert!(eq_enc("{A={B=ci}ci}", &enc)); assert!(!eq_enc("{A={B=ci}ci", &enc)); - } #[test]