Skip to content

Commit

Permalink
Merge pull request #4 from DrAma999/feature/DSL
Browse files Browse the repository at this point in the history
DSL Style for Byte Array Creation
  • Loading branch information
DrAma999 authored Feb 7, 2022
2 parents 3bb8487 + 99dd83a commit f3fcff9
Show file tree
Hide file tree
Showing 8 changed files with 502 additions and 1 deletion.
44 changes: 44 additions & 0 deletions Sources/BitWiser/DSL/ByteArrayBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// ByteArrayBuilder.swift
//
//
// Created by Andrea Finollo on 30/01/22.
//

import Foundation


/// `ByteArrayBuilder` is a `@resultBuilder` to create sequence of `Byte` in a DSL style
@resultBuilder
public enum ByteArrayBuilder {

public static func buildBlock(_ components: [Byte]...) -> [Byte] {
components.flatMap { $0 }
}

public static func buildExpression(_ expression: [Byte]) -> [Byte] {
expression
}

public static func buildExpression(_ expression: Byte) -> [Byte] {
[expression]
}

public static func buildOptional(_ component: [Byte]?) -> [Byte] {
component ?? []
}

public static func buildEither(first component: [Byte]) -> [Byte] {
component
}

public static func buildEither(second component: [Byte]) -> [Byte] {
component
}

public static func buildArray(_ components: [[Byte]]) -> [Byte] {
return components.flatMap { $0 }
}


}
55 changes: 55 additions & 0 deletions Sources/BitWiser/DSL/DataConvertibleBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// DataConvertibleBuilder.swift
//
//
// Created by Andrea Finollo on 04/02/22.
//

import Foundation

/// `DataConvertibleBuilder` is a `@resultBuilder` to create `Data` in a DSL style
@resultBuilder
public enum DataConvertibleBuilder {

public static func buildBlock(_ components: DataConvertible...) -> Data {
return components.reduce(Data()) { partialResult, value in
var mutBuffer = partialResult
mutBuffer.append(value.data)
return mutBuffer
}
}

public static func buildExpression(_ expression: [DataConvertible]) -> Data {
return expression.reduce(Data()) { partialResult, value in
var mutBuffer = partialResult
mutBuffer.append(value.data)
return mutBuffer
}
}

public static func buildExpression(_ expression: DataConvertible) -> Data {
expression.data
}

public static func buildOptional(_ component: DataConvertible?) -> Data {
return component?.data ?? Data()
}

public static func buildEither(first component: DataConvertible) -> Data {
return component.data
}

public static func buildEither(second component: DataConvertible) -> Data {
return component.data
}

public static func buildArray(_ components: [DataConvertible]) -> Data {
return components
.reduce(Data()) { partialResult, value in
var mutBuffer = partialResult
mutBuffer.append(value.data)
return mutBuffer
}
}

}
18 changes: 18 additions & 0 deletions Sources/BitWiser/DataRepresentable/DataRepresentable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// DataRepresentable.swift
//
//
// Created by Andrea Finollo on 04/02/22.
//

import Foundation

public protocol ExpressibleByData {
init?(data: Data)
}

public protocol DataConvertible {
var data: Data { get }
}

public typealias DataRepresentable = ExpressibleByData & DataConvertible
31 changes: 31 additions & 0 deletions Sources/BitWiser/Extension/Extension+Data.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ extension Data: ByteRepresentable {
}
}

extension Data : DataConvertible {

public init?(data: Data) {
self.init(data)
}

public var data: Data {
return self
}
}

public extension Data {

/// Option about how to encode the hex string representation
Expand All @@ -44,3 +55,23 @@ public extension Data {
}.joined(separator: padding)
}
}

public extension Data {

/// Initialize a `Data` with a `@DataConvertibleBuilder`.
///
/// Data {
/// [Byte(0x00)]
/// Byte(0x01)
/// 0x02
/// "\u{03}"
/// CustomObject()
/// }
///
/// - parameter representables: A DSL closure with `DataRepresentable`s. Object passed in the closure must conform to `DataRepresentable` protocol.
/// - Note: Objects passed in the closure can have different `Data` lenght.
/// - Important: Always start from the LSB.
init(@DataConvertibleBuilder _ representables: () -> Data) {
self.init(representables())
}
}
45 changes: 45 additions & 0 deletions Sources/BitWiser/Extension/Extension+Numeric.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@

import Foundation

extension DataConvertible where Self: ExpressibleByIntegerLiteral{
public init?(data: Data) {
var value: Self = 0
guard data.count == MemoryLayout.size(ofValue: value) else { return nil }
_ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
self = value
}

public var data: Data {
return withUnsafeBytes(of: self) { Data($0) }
}
}

extension Int8: DataConvertible { }
extension Int16: DataConvertible { }
extension Int32: DataConvertible { }
extension Int64: DataConvertible { }
extension Int: DataConvertible { }

extension Float: DataConvertible { }
extension Double: DataConvertible { }

extension UInt8: DataConvertible { }
extension UInt16: DataConvertible { }
extension UInt32: DataConvertible { }
extension UInt64: DataConvertible { }
extension UInt: DataConvertible { }

// MARK: - Signed Numeric

extension Int16: ByteConvertible {
Expand Down Expand Up @@ -103,3 +131,20 @@ extension Array where Element == Byte {
return or
}
}

public extension Array where Element == Byte {
/// Initialize a `[Byte]`Array with a `@ByteArrayBuilder`.
///
/// Array<Byte> {
/// [Byte(0x00)]
/// Byte(0x01)
/// 0x02
/// [UInt8(0x03)]
/// }
///
/// - parameter builder: A DSL closure with `Byte`s.
/// - Important: Always start from the LSB
init(@ByteArrayBuilder _ builder: () -> [Byte]) {
self.init(builder())
}
}
12 changes: 12 additions & 0 deletions Sources/BitWiser/Extension/Extension+String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@

import Foundation

extension String: DataConvertible {

public init?(data: Data) {
self.init(data: data, encoding: .utf8)
}

public var data: Data {
// Note: a conversion to UTF-8 cannot fail.
return Data(self.utf8)
}
}

public extension String {
/// Given a hex string it returns a Data
/// - Returns: a `Data` value
Expand Down
2 changes: 1 addition & 1 deletion Tests/BitWiserTests/ByteTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ByteTests: XCTestCase {
var hexDescription = value.hexDescription
XCTAssertTrue(binDescription == "00000000")
XCTAssertTrue(hexDescription == "00")

value = 0b11111111
binDescription = value.binaryDescription
hexDescription = value.hexDescription
Expand Down
Loading

0 comments on commit f3fcff9

Please sign in to comment.