Skip to content

Commit 3b8d5de

Browse files
authored
Add Matchable protocol and documentation (#9)
This PR provides a more general protocol `Matchable` along side of `MatchableRawRepresentable` and provides a conformance when the `RawValue` conforms to `Matchable`. More documentation is provided.
1 parent 5b19c2a commit 3b8d5de

File tree

9 files changed

+353
-42
lines changed

9 files changed

+353
-42
lines changed

README.md

Lines changed: 121 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ A declarative Swift package for parsing binary data using macros, built on top o
1414
- **Skip functionality**: Skip unwanted bytes with documentation
1515
- **Custom byte counts**: Parse types with specific byte lengths
1616
- **Remaining data parsing**: Parse all remaining bytes in a buffer
17+
- **Enum parsing**: Parse enums with pattern matching and associated values
18+
19+
## Requirements
20+
21+
- Swift 6.2+
22+
- Xcode 26.0+
23+
- macOS 15.0+ / iOS 18.0+ / tvOS 18.0+ / watchOS 11.0+ / visionOS 2.0+
1724

1825
## Installation
1926

@@ -107,7 +114,9 @@ print(packet.payload.message) // "hello world!"
107114

108115
## Usage
109116

110-
### Basic Parsing
117+
### Struct Parsing
118+
119+
#### Struct Parsing Macros
111120

112121
Mark your struct with `@ParseStruct` and annotate fields with parsing macros:
113122

@@ -125,27 +134,17 @@ struct Header {
125134
}
126135
```
127136

128-
### Parsing Macros
137+
Available struct parsing macros:
129138

139+
- **`@ParseStruct`** - Mark a struct for binary parsing
130140
- **`@parse`** - Use the type's default parsing behavior
131141
- **`@parse(endianness: .big/.little)`** - Parse with specific endianness
132142
- **`@parse(byteCount: Int)`** - Parse a specific number of bytes
133143
- **`@parse(byteCountOf: KeyPath)`** - Parse bytes based on another field's value
134144
- **`@parseRest()`** - Parse all remaining bytes
135145
- **`@skip(byteCount: Int, because: String)`** - Skip bytes with documentation
136146

137-
### Protocol Conformances
138-
139-
BinaryParseKit defines four parsing protocols:
140-
141-
- **`Parsable`** - Basic parsing without additional parameters
142-
- **`EndianParsable`** - Parsing with endianness specification
143-
- **`SizedParsable`** - Parsing with byte count specification
144-
- **`EndianSizedParsable`** - Parsing with both endianness and byte count
145-
146-
Most built-in types already conform to these protocols. For custom types, implement the appropriate protocol(s).
147-
148-
### Variable-Length Fields
147+
#### Variable-Length Fields
149148

150149
Parse fields whose length depends on previously parsed values:
151150

@@ -160,8 +159,6 @@ struct VariableMessage {
160159
}
161160
```
162161

163-
### Complex Structures
164-
165162
Combine multiple techniques for complex binary formats:
166163

167164
```swift
@@ -182,11 +179,111 @@ struct ComplexPacket {
182179
}
183180
```
184181

185-
## Requirements
182+
### Enum Parsing
186183

187-
- Swift 6.2+
188-
- Xcode 26.0+
189-
- macOS 15.0+ / iOS 18.0+ / tvOS 18.0+ / watchOS 11.0+ / visionOS 2.0+
184+
#### Enum Parsing Macros
185+
186+
Parse enums with pattern matching and associated values using the `@ParseEnum` macro:
187+
188+
**Basic Enum Matching**
189+
190+
```swift
191+
@ParseEnum
192+
enum MessageType {
193+
@match(byte: 0x01)
194+
case connect
195+
196+
@match(bytes: [0x02, 0x03])
197+
case data
198+
199+
@match(byte: 0xFF)
200+
case disconnect
201+
}
202+
203+
let msgType = try MessageType(parsing: Data([0x01]))
204+
// msgType == .connect
205+
```
206+
207+
**Enums with Associated Values**
208+
209+
Use `@matchAndTake` to consume the match pattern and `@parse` to parse associated values:
210+
211+
```swift
212+
@ParseEnum
213+
enum Command: Equatable {
214+
@matchAndTake(byte: 0x01)
215+
@parse(endianness: .big)
216+
case setValue(UInt16)
217+
218+
@matchAndTake(byte: 0x02)
219+
@parse(endianness: .big)
220+
@parse(endianness: .big)
221+
case setRange(start: UInt16, end: UInt16)
222+
223+
@matchAndTake(byte: 0xFF)
224+
case reset
225+
}
226+
227+
let cmd = try Command(parsing: Data([0x01, 0x12, 0x34]))
228+
// cmd == .setValue(0x1234)
229+
```
230+
231+
**Raw Representable Enums**
232+
233+
For enums with raw values, conform to `MatchableRawRepresentable` (or `Matchable`):
234+
235+
```swift
236+
@ParseEnum
237+
enum StatusCode: UInt8, MatchableRawRepresentable {
238+
@match
239+
case success = 0x00
240+
241+
@match
242+
case error
243+
244+
@match
245+
case pending
246+
}
247+
```
248+
249+
**Default Cases**
250+
251+
Use `@matchDefault` to handle unrecognized values:
252+
253+
```swift
254+
@ParseEnum
255+
enum PacketType {
256+
@match(byte: 0x01)
257+
case known
258+
259+
@matchDefault
260+
case unknown
261+
}
262+
```
263+
264+
Available enum parsing macros:
265+
266+
- **`@ParseEnum`** - Mark an enum for binary parsing
267+
- **`@match(byte: UInt8)`** - Match a single byte
268+
- **`@match(bytes: [UInt8])`** - Match a sequence of bytes, but doesn't shrink the remaining buffer
269+
- **`@matchAndTake(byte:)`** and **`@matchAndTake(bytes:)`** - Match and consume bytes in the remaining buffer
270+
- **`@matchDefault`** - Default case for unrecognized patterns, which doesn't consume any bytes in the remaining buffer
271+
- **`@parse`** - Parse associated values (same options as struct fields)
272+
- **`@skip`** - Skip bytes (same options as struct fields)
273+
274+
### Protocol Conformances
275+
276+
BinaryParseKit defines four parsing protocols:
277+
278+
- **`Parsable`** - Basic parsing without additional parameters
279+
- **`EndianParsable`** - Parsing with endianness specification
280+
- **`SizedParsable`** - Parsing with byte count specification
281+
- **`EndianSizedParsable`** - Parsing with both endianness and byte count
282+
283+
Most built-in types already conform to these protocols. For custom types, implement the appropriate protocol(s).
284+
285+
In addition, as mentioned in the previous enum parsing section,
286+
`Matchable` and `MatchableRawRepresentable` is introduced to allow each case to provide bytes for matching in the process.
190287

191288
## Contributing
192289

@@ -208,10 +305,10 @@ If you encounter any issues, have feature requests, or want to suggest improveme
208305

209306
Some areas we're considering for future development:
210307

211-
- **Enum parsing** - Support for parsing enums with associated values
212-
- **More convenient APIs** - Shorter syntax and better autocomplete support
213-
- **Advanced validation** - Runtime validation of parsing constraints
214-
- **Performance optimizations** - Further optimization of generated parsing code
308+
- **More convenient APIs** - Shorter syntax and better autocomplete support, such as merging `ParseStruct` and `ParseEnum`
309+
- **Advanced validation** - Runtime validation of parsing constraints, such as require minimal byte size checking in front instead of at each parsing
310+
- **Performance optimizations** - Further optimization of generated parsing code, such as linear time enum matching for constant bytes provided (`O( max(n, m) )` instead of `O(n * m)`, where `n` is the number of cases and `m` is the max number of bytes provided for each case)
311+
- **Porting to prio iOS/macOS 26** - because `Span` is introduced only in iOS/macOS 26, port of using `withUnsafePointer` can be provided to prior versions of OSes for better compatibility
215312

216313
Your feedback on these directions and other ideas is highly appreciated!
217314

0 commit comments

Comments
 (0)