/// A patch to some part of a `Syntax` tree.
public enum Patch: CustomDebugStringConvertible, CustomDocConvertible {
case Replace(A, A)
case Insert(A)
case Delete(A)
public var state: (before: A?, after: A?) {
switch self {
case let .Replace(a, b):
return (a, b)
case let .Insert(b):
return (nil, b)
case let .Delete(a):
return (a, nil)
}
}
public var inverse: Patch {
switch self {
case let .Replace(a, b):
return .Replace(b, a)
case let .Insert(b):
return .Delete(b)
case let .Delete(a):
return .Insert(a)
}
}
// MARK: CustomDebugStringConvertible
public var debugDescription: String {
switch self {
case let .Replace(a, b):
return ".Replace(\(String(reflecting: a)), \(String(reflecting: b)))"
case let .Insert(b):
return ".Insert(\(String(reflecting: b)))"
case let .Delete(a):
return ".Delete(\(String(reflecting: a)))"
}
}
// MARK: CustomDocConvertible
public var doc: Doc {
return (state.before.map(Doc.init)?.bracket("{-", "-}") ?? .Empty)
<> (state.after.map(Doc.init)?.bracket("{+", "+}") ?? .Empty)
}
}
// MARK: - Equality
extension Patch {
public static func equals(param: (A, A) -> Bool)(_ left: Patch, _ right: Patch) -> Bool {
return Optional.equals(param)(left.state.before, right.state.before)
&& Optional.equals(param)(left.state.after, right.state.after)
}
}
// MARK: - Hashing
extension Patch {
public func hash(param: A -> Hash) -> Hash {
return Hash.Ordered([
state.before.map(param) ?? Hash.Empty,
state.after.map(param) ?? Hash.Empty
])
}
}
// MARK: - JSON
extension Patch {
public func JSON(ifLeaf: A -> Doubt.JSON) -> Doubt.JSON {
switch self {
case let .Replace(a, b):
return [
"replace": [
"before": ifLeaf(a),
"after": ifLeaf(b),
]
]
case let .Insert(b):
return [
"insert": ifLeaf(b),
]
case let .Delete(a):
return [
"delete": ifLeaf(a)
]
}
}
}
extension Patch where A: CustomJSONConvertible {
public var JSON: Doubt.JSON {
return JSON { $0.JSON }
}
}
/// A hack to enable constrained extensions on `Free>`.
public protocol PatchConvertible {
typealias Element
init(patch: Patch)
var patch: Patch { get }
}
extension Patch: PatchConvertible {
public init(patch: Patch) { self = patch }
public var patch: Patch { return self }
}