Skip to content

Instantly share code, notes, and snippets.

@kaqu
Created February 26, 2020 21:04
Show Gist options
  • Save kaqu/31d68d2633329f1ded0ada31323dad9d to your computer and use it in GitHub Desktop.
Save kaqu/31d68d2633329f1ded0ada31323dad9d to your computer and use it in GitHub Desktop.
Swift dynamic parameters
internal protocol AnyParameter {
var name: String { get }
var optional: Bool { get }
var type: Any.Type { get }
}
public struct Parameter<Value>: AnyParameter {
public var name: String
public var optional: Bool
public var `default`: Value?
internal var type: Any.Type { Value.self }
public init(_ name: String, optional: Bool = false, default: Value? = nil) {
self.name = name
self.optional = optional
self.default = `default`
}
}
public struct Parameters {
private var parameters: Array<(AnyParameter, value: Any?)> = .init()
public mutating func require<Value>(_ parameterName: String, of type: Value.Type, optional: Bool = false, default: Value? = nil) {
let parameter: Parameter<Value> = .init(parameterName, optional: optional, default: `default`)
if let currentIndex = parameters.firstIndex(where: { $0.0.name == parameterName }) {
parameters[currentIndex] = (parameter, parameter.default)
} else {
parameters.append((parameter, parameter.default))
}
}
public mutating func require<Value>(_ parameter: Parameter<Value>) {
if let currentIndex = parameters.firstIndex(where: { $0.0.name == parameter.name }) {
parameters[currentIndex] = (parameter, parameter.default)
} else {
parameters.append((parameter, parameter.default))
}
}
public mutating func remove<Value>(_ parameter: Parameter<Value>) {
guard let currentIndex = parameters.firstIndex(where: { $0.0.name == parameter.name }) else { return }
parameters.remove(at: currentIndex)
}
public func value<Value>(for parameter: Parameter<Value>) -> Value? {
guard let param = parameters.first(where: { $0.0.name == parameter.name }) else { return nil }
assert(param.0.type == parameter.type, "Required parameter value is not matching requested")
guard let value = param.value else { return nil }
assert(value is Value, "Stored parameter value is not matching required")
return value as? Value
}
public func value<Value>(of type: Value.Type = Value.self, for parameterName: String) -> Value? {
guard let param = parameters.first(where: { $0.0.name == parameterName }) else { return nil }
assert(param.0.type == type, "Required parameter value is not matching requested")
guard let value = param.value else { return nil }
assert(value is Value, "Stored parameter value is not matching required")
return value as? Value
}
public mutating func set<Value>(value: Value?, for parameter: Parameter<Value>) {
guard let index = parameters.firstIndex(where: { $0.0.name == parameter.name }) else { return assertionFailure("Trying to set not required parameter value") }
assert(parameters[index].0.type == parameter.type, "Required parameter value is not matching setted")
parameters[index].value = value
}
public mutating func set<Value>(value: Value?, for parameterName: String) {
guard let index = parameters.firstIndex(where: { $0.0.name == parameterName }) else { return assertionFailure("Trying to set not required parameter value") }
assert(parameters[index].0.type == Value.self, "Required parameter value is not matching setted")
parameters[index].value = value
}
public subscript<Value>(_ parameter: Parameter<Value>) -> Value? {
get {
value(for: parameter)
}
set {
set(value: newValue, for: parameter)
}
}
public subscript<Value>(_ parameterName: String) -> Value? {
get {
value(for: parameterName)
}
set {
set(value: newValue, for: parameterName)
}
}
public var allProvided: Bool {
parameters.reduce(into: true, { result, parameter in
result = result && (parameter.0.optional || parameter.value != nil)
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment