Skip to content

Commit

Permalink
Added: optional properties by key path
Browse files Browse the repository at this point in the history
  • Loading branch information
ikhvorost committed May 14, 2022
1 parent 5e69eb0 commit da91454
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 147 deletions.
2 changes: 1 addition & 1 deletion Sources/KeyValueCoding/Accessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protocol Accessor {}

extension Accessor {

static func get(from pointer: UnsafeRawPointer) -> Any? {
static func get(from pointer: UnsafeRawPointer) -> Any {
return pointer.assumingMemoryBound(to: Self.self).pointee
}

Expand Down
40 changes: 13 additions & 27 deletions Sources/KeyValueCoding/KeyValueCoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@
// THE SOFTWARE.
//

fileprivate extension Array {
mutating func popFirst() -> Element? {
guard count > 0 else {
return nil
}
return removeFirst()
}
}

fileprivate func withPointer<T>(_ instance: inout T, _ body: (UnsafeMutableRawPointer, Metadata) -> Any?) -> Any? {
withUnsafePointer(to: &instance) {
let metadata = swift_metadata(of: T.self)
Expand Down Expand Up @@ -70,22 +61,23 @@ fileprivate func withPointer<T>(_ instance: inout T, _ body: (UnsafeMutableRawPo
}

@discardableResult
fileprivate func withProperty<T>(_ instance: inout T, path: [String], _ body: (Accessor.Type, UnsafeMutableRawPointer) -> Any?) -> Any? {
fileprivate func withProperty<T>(_ instance: inout T, path: [String], _ body: (Metadata, UnsafeMutableRawPointer) -> Any?) -> Any? {
withPointer(&instance) { pointer, metadata in
var keys = path
guard let key = keys.popFirst(), let property = (metadata.properties.first { $0.name == key }) else {
guard let key = keys.popLast(), let property = (metadata.properties.first { $0.name == key }) else {
return nil
}

let pointer = pointer.advanced(by: property.offset)

if keys.isEmpty {
return body(property.metadata.accessor, pointer)
return body(property.metadata, pointer)
}
else if var value = property.metadata.accessor.get(from: pointer) {
else if var value = property.metadata.get(from: pointer) {
defer {
if property.metadata.kind == .struct {
property.metadata.accessor.set(value: value, pointer: pointer)
let metadata = swift_metadata(of: type(of: value))
if metadata.kind == .struct {
property.metadata.set(value: value, pointer: pointer)
}
}
return withProperty(&value, path: keys, body)
Expand Down Expand Up @@ -122,12 +114,9 @@ public func swift_metadata(of instance: Any) -> Metadata {
/// - key: The name of one of the instance's properties.
/// - Returns: The value for the property identified by key.
public func swift_value<T>(of instance: inout T, key: String) -> Any? {
let path = key.components(separatedBy: ".")
guard path.count > 0 else {
return nil
}
return withProperty(&instance, path: path) { accessor, pointer in
accessor.get(from: pointer)
let path: [String] = key.components(separatedBy: ".").reversed()
return withProperty(&instance, path: path) { metadata, pointer in
metadata.get(from: pointer)
}
}

Expand All @@ -138,12 +127,9 @@ public func swift_value<T>(of instance: inout T, key: String) -> Any? {
/// - value: The value for the property identified by key.
/// - key: The name of one of the instance's properties.
public func swift_setValue<T>(_ value: Any?, to: inout T, key: String) {
let path = key.components(separatedBy: ".")
guard path.count > 0 else {
return
}
withProperty(&to, path: path) { accessor, pointer in
accessor.set(value: value as Any, pointer: pointer)
let path: [String] = key.components(separatedBy: ".").reversed()
withProperty(&to, path: path) { metadata, pointer in
metadata.set(value: value as Any, pointer: pointer)
}
}

Expand Down
26 changes: 18 additions & 8 deletions Sources/KeyValueCoding/Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,14 @@ public struct Metadata {

private let container: ProtocolTypeContainer

var accessor: Accessor.Type { container.accessor }

/// Type.
public let type: Any.Type

/// Kind of the type.
public let kind: Kind

/// Size of the type.
public var size: Int { accessor.size }
public var size: Int { container.accessor.size }

/// Accessible properties of the type.
public let properties: [Property]
Expand All @@ -128,15 +126,11 @@ public struct Metadata {
var fieldMetadata = _FieldReflectionMetadata()
return (0..<count).compactMap {
let propertyType = swift_reflectionMirror_recursiveChildMetadata(type, index: $0, fieldMetadata: &fieldMetadata)

defer { fieldMetadata.freeFunc?(fieldMetadata.name) }
guard let propertyName = fieldMetadata.name.flatMap({ String(validatingUTF8: $0) }) else {
return nil
}

let offset = swift_reflectionMirror_recursiveChildOffset(type, index: $0)

return Property(name: propertyName,
return Property(name: String(cString: fieldMetadata.name!),
isStrong: fieldMetadata.isStrong,
isVar: fieldMetadata.isVar,
offset: offset,
Expand All @@ -150,6 +144,22 @@ public struct Metadata {
self.container = ProtocolTypeContainer(type: type)
self.properties = Self.enumProperties(type: type, kind: self.kind)
}

func get(from pointer: UnsafeRawPointer) -> Any? {
let value = container.accessor.get(from: pointer)

// Optional
if kind == .optional {
let mirror = Mirror(reflecting: value)
return mirror.children.first?.value
}

return value
}

func set(value: Any, pointer: UnsafeMutableRawPointer) {
container.accessor.set(value: value as Any, pointer: pointer)
}
}

class MetadataCache {
Expand Down
Loading

0 comments on commit da91454

Please sign in to comment.