2015-10-02 23:36:05 +03:00
|
|
|
/// A patch to some part of a `Syntax` tree.
|
2015-10-07 16:54:28 +03:00
|
|
|
public enum Patch<A>: CustomDebugStringConvertible, CustomDocConvertible {
|
2015-10-06 23:31:44 +03:00
|
|
|
case Replace(Fix<A>, Fix<A>)
|
|
|
|
case Insert(Fix<A>)
|
|
|
|
case Delete(Fix<A>)
|
2015-10-02 23:53:43 +03:00
|
|
|
|
2015-10-02 23:38:21 +03:00
|
|
|
public var state: (before: Fix<A>?, after: Fix<A>?) {
|
2015-10-02 23:38:02 +03:00
|
|
|
switch self {
|
|
|
|
case let .Replace(a, b):
|
|
|
|
return (a, b)
|
2015-10-06 23:31:44 +03:00
|
|
|
case let .Insert(b):
|
|
|
|
return (nil, b)
|
|
|
|
case let .Delete(a):
|
|
|
|
return (a, nil)
|
2015-10-02 23:38:02 +03:00
|
|
|
}
|
|
|
|
}
|
2015-10-06 23:45:36 +03:00
|
|
|
|
|
|
|
|
2015-10-07 00:07:16 +03:00
|
|
|
/// The cost of a patch to the diff.
|
|
|
|
public var cost: Int {
|
|
|
|
switch self {
|
|
|
|
case .Replace:
|
|
|
|
return 2
|
|
|
|
default:
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-07 15:41:54 +03:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-06 23:45:36 +03:00
|
|
|
// 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)))"
|
|
|
|
}
|
|
|
|
}
|
2015-10-07 16:54:28 +03:00
|
|
|
|
|
|
|
|
|
|
|
// MARK: CustomDocConvertible
|
|
|
|
|
|
|
|
public var doc: Doc {
|
|
|
|
return (state.before?.doc.bracket("{-", "-}") ?? .Empty)
|
|
|
|
<> (state.after?.doc.bracket("{+", "+}") ?? .Empty)
|
|
|
|
}
|
2015-10-02 23:34:00 +03:00
|
|
|
}
|
2015-10-02 23:49:04 +03:00
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Equality
|
|
|
|
|
|
|
|
extension Patch {
|
|
|
|
public static func equals(param: (A, A) -> Bool)(_ left: Patch, _ right: Patch) -> Bool {
|
|
|
|
return Optional.equals(Fix.equals(param))(left.state.before, right.state.before)
|
|
|
|
&& Optional.equals(Fix.equals(param))(left.state.after, right.state.after)
|
|
|
|
}
|
|
|
|
}
|
2015-10-02 23:50:59 +03:00
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Hashing
|
|
|
|
|
|
|
|
extension Patch {
|
|
|
|
public func hash(param: A -> Hash) -> Hash {
|
|
|
|
return Hash.Ordered([
|
|
|
|
state.before.map { $0.hash(param) } ?? Hash.Empty,
|
|
|
|
state.after.map { $0.hash(param) } ?? Hash.Empty
|
|
|
|
])
|
|
|
|
}
|
|
|
|
}
|
2015-10-07 04:24:01 +03:00
|
|
|
|
|
|
|
|
2015-10-08 14:28:03 +03:00
|
|
|
// MARK: - JSONConvertible
|
|
|
|
|
|
|
|
extension Patch {
|
|
|
|
public func JSON(ifLeaf: A -> Doubt.JSON) -> Doubt.JSON {
|
|
|
|
switch self {
|
|
|
|
case let .Replace(a, b):
|
|
|
|
return Doubt.JSON.Dictionary([
|
2015-10-08 14:31:43 +03:00
|
|
|
"replace": Doubt.JSON.Dictionary([
|
|
|
|
"before": a.JSON(ifLeaf),
|
|
|
|
"after": b.JSON(ifLeaf),
|
|
|
|
])
|
2015-10-08 14:28:03 +03:00
|
|
|
])
|
|
|
|
case let .Insert(b):
|
|
|
|
return Doubt.JSON.Dictionary([
|
2015-10-08 14:31:43 +03:00
|
|
|
"insert": b.JSON(ifLeaf),
|
2015-10-08 14:28:03 +03:00
|
|
|
])
|
|
|
|
case let .Delete(a):
|
|
|
|
return Doubt.JSON.Dictionary([
|
2015-10-08 14:31:43 +03:00
|
|
|
"delete": a.JSON(ifLeaf)
|
2015-10-08 14:28:03 +03:00
|
|
|
])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-07 04:25:21 +03:00
|
|
|
/// A hack to enable constrained extensions on `Free<A, Patch<A>>`.
|
2015-10-07 04:24:01 +03:00
|
|
|
public protocol PatchConvertible {
|
|
|
|
typealias Info
|
|
|
|
|
|
|
|
init(patch: Patch<Info>)
|
|
|
|
var patch: Patch<Info> { get }
|
|
|
|
}
|
2015-10-07 04:24:43 +03:00
|
|
|
|
|
|
|
extension Patch: PatchConvertible {
|
|
|
|
public init(patch: Patch) { self = patch }
|
|
|
|
public var patch: Patch { return self }
|
|
|
|
}
|