From a40e37d40efc94f3838029e3ef8e1a71945617c5 Mon Sep 17 00:00:00 2001 From: alexjg Date: Mon, 3 Jul 2023 00:42:27 +0100 Subject: [PATCH] Update to automerge@0.5.0, implement marks (#39) * Update to automerge@0.5.0, implement marks There are some mechanical changes to the API: * Allow negative `delete` arguments for `Doc.splice` and `Doc.spliceText` * Add `Doc.mark`, `Doc.marks` and `Doc.marksAt` * Add the `Mark` and `ExpandMark` types * Add PatchAction.Conflict and PatchAction.Marks * Add a `marks` field to `PatchAction.SpliceText` and `PatchAction.Insert` One slightly more significant change is the addition of `unsafe impl Send{}` and `unsafe impl Sync{}` on the `Doc` we export to uniffi. This is required because currently `automerge::Automerge` is not `Send` or `Sync` due to the internal use of an `Rc`. The `unsafe` impls are okay because we guarantee that all accesses of the document on the Swift side are from a single thread. In future we should be able to remove the `Rc` from automerge to make it `Send` again and remove these unsafe impls. * fix: removes extraneous whitespace `rustfmt` complains about. * fix: sets LOCAL_BUILD environment variable in CI so that swift package uses the locally built XCFramework. Thought this was working, but with errors on the swift file, I think I missed it earlier. * docs: updates documentation to curate Marks into Document and overall Automerge-Swift documentation --------- Co-authored-by: Joe Heck --- .github/workflows/ci.yaml | 2 + AutomergeUniffi/automerge.swift | 1589 +++++++++++------ AutomergeUniffi/automergeFFI.h | 585 +++--- Sources/Automerge/Automerge.docc/Automerge.md | 2 + Sources/Automerge/Automerge.docc/Document.md | 4 + Sources/Automerge/Document.swift | 92 +- Sources/Automerge/Marks.swift | 79 + Sources/Automerge/Patch.swift | 46 +- Tests/AutomergeTests/TestMarks.swift | 54 + rust/Cargo.lock | 116 +- rust/Cargo.toml | 6 +- rust/src/automerge.udl | 31 +- rust/src/doc.rs | 100 +- rust/src/lib.rs | 2 + rust/src/mark.rs | 41 + rust/src/patches.rs | 171 +- rust/src/patches/observer.rs | 330 ---- rust/src/patches/sequence_tree.rs | 492 ----- 18 files changed, 1888 insertions(+), 1854 deletions(-) create mode 100644 Sources/Automerge/Marks.swift create mode 100644 Tests/AutomergeTests/TestMarks.swift create mode 100644 rust/src/mark.rs delete mode 100644 rust/src/patches/observer.rs delete mode 100644 rust/src/patches/sequence_tree.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c89bc43d..da3be019 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,6 +3,8 @@ on: [push] jobs: build-test: runs-on: macos-latest + env: + LOCAL_BUILD: true steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 diff --git a/AutomergeUniffi/automerge.swift b/AutomergeUniffi/automerge.swift index a8bc7923..b073fe38 100644 --- a/AutomergeUniffi/automerge.swift +++ b/AutomergeUniffi/automerge.swift @@ -19,13 +19,13 @@ private extension RustBuffer { } static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer { - try! rustCall { ffi_automerge_9b9_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) } + try! rustCall { ffi_automerge_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) } } // Frees the buffer in place. // The buffer must not be used after this is called. func deallocate() { - try! rustCall { ffi_automerge_9b9_rustbuffer_free(self, $0) } + try! rustCall { ffi_automerge_rustbuffer_free(self, $0) } } } @@ -104,12 +104,12 @@ private func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: // Reads a float at the current offset. private func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float { - try Float(bitPattern: readInt(&reader)) + Float(bitPattern: try readInt(&reader)) } // Reads a float at the current offset. private func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double { - try Double(bitPattern: readInt(&reader)) + Double(bitPattern: try readInt(&reader)) } // Indicates if the offset has reached the end of the buffer. @@ -239,38 +239,49 @@ private extension RustCallStatus { } private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T { - try makeRustCall(callback, errorHandler: { - $0.deallocate() - return UniffiInternalError.unexpectedRustCallError - }) + try makeRustCall(callback, errorHandler: nil) } -private func rustCallWithError -(_ errorFfiConverter: F.Type, _ callback: (UnsafeMutablePointer) -> T) throws -> T - where F.SwiftType: Error, F.FfiType == RustBuffer -{ - try makeRustCall(callback, errorHandler: { try errorFfiConverter.lift($0) }) +private func rustCallWithError( + _ errorHandler: @escaping (RustBuffer) throws -> Error, + _ callback: (UnsafeMutablePointer) -> T +) throws -> T { + try makeRustCall(callback, errorHandler: errorHandler) } private func makeRustCall( _ callback: (UnsafeMutablePointer) -> T, - errorHandler: (RustBuffer) throws -> Error + errorHandler: ((RustBuffer) throws -> Error)? ) throws -> T { + uniffiEnsureInitialized() var callStatus = RustCallStatus() let returnedVal = callback(&callStatus) + try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler) + return returnedVal +} + +private func uniffiCheckCallStatus( + callStatus: RustCallStatus, + errorHandler: ((RustBuffer) throws -> Error)? +) throws { switch callStatus.code { case CALL_SUCCESS: - return returnedVal + return case CALL_ERROR: - throw try errorHandler(callStatus.errorBuf) + if let errorHandler = errorHandler { + throw try errorHandler(callStatus.errorBuf) + } else { + callStatus.errorBuf.deallocate() + throw UniffiInternalError.unexpectedRustCallError + } case CALL_PANIC: // When the rust code sees a panic, it tries to construct a RustBuffer // with the message. But if that code panics, then it just sends back // an empty buffer. if callStatus.errorBuf.len > 0 { - throw try UniffiInternalError.rustPanic(FfiConverterString.lift(callStatus.errorBuf)) + throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf)) } else { callStatus.errorBuf.deallocate() throw UniffiInternalError.rustPanic("Rust panic") @@ -384,7 +395,7 @@ private struct FfiConverterString: FfiConverter { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String { let len: Int32 = try readInt(&buf) - return try String(bytes: readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)! + return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)! } public static func write(_ value: String, into buf: inout [UInt8]) { @@ -405,8 +416,11 @@ public protocol DocProtocol { func putObjectInList(obj: ObjId, index: UInt64, objType: ObjType) throws -> ObjId func insertInList(obj: ObjId, index: UInt64, value: ScalarValue) throws func insertObjectInList(obj: ObjId, index: UInt64, objType: ObjType) throws -> ObjId - func spliceText(obj: ObjId, start: UInt64, delete: UInt64, chars: String) throws - func splice(obj: ObjId, start: UInt64, delete: UInt64, values: [ScalarValue]) throws + func spliceText(obj: ObjId, start: UInt64, delete: Int64, chars: String) throws + func splice(obj: ObjId, start: UInt64, delete: Int64, values: [ScalarValue]) throws + func mark(obj: ObjId, start: UInt64, end: UInt64, expand: ExpandMark, name: String, value: ScalarValue) throws + func marks(obj: ObjId) throws -> [Mark] + func marksAt(obj: ObjId, heads: [ChangeHash]) throws -> [Mark] func deleteInMap(obj: ObjId, key: String) throws func deleteInList(obj: ObjId, index: UInt64) throws func incrementInMap(obj: ObjId, key: String, by: Int64) throws @@ -455,48 +469,36 @@ public class Doc: DocProtocol { } public convenience init() { - self.init( - unsafeFromRawPointer: try! - - rustCall { - automerge_9b9_Doc_new($0) - } - ) + self.init(unsafeFromRawPointer: try! rustCall { + uniffi_automerge_fn_constructor_doc_new($0) + }) } deinit { - try! rustCall { ffi_automerge_9b9_Doc_object_free(pointer, $0) } + try! rustCall { uniffi_automerge_fn_free_doc(pointer, $0) } } public static func newWithActor(actor: ActorId) -> Doc { - Doc( - unsafeFromRawPointer: try! - - rustCall { - automerge_9b9_Doc_new_with_actor( - FfiConverterTypeActorId.lower(`actor`), $0 - ) - } - ) + Doc(unsafeFromRawPointer: try! rustCall { + uniffi_automerge_fn_constructor_doc_new_with_actor( + FfiConverterTypeActorId.lower(`actor`), $0 + ) + }) } public static func load(bytes: [UInt8]) throws -> Doc { - try Doc( - unsafeFromRawPointer: - - rustCallWithError(FfiConverterTypeLoadError.self) { - automerge_9b9_Doc_load( - FfiConverterSequenceUInt8.lower(bytes), $0 - ) - } - ) + Doc(unsafeFromRawPointer: try rustCallWithError(FfiConverterTypeLoadError.lift) { + uniffi_automerge_fn_constructor_doc_load( + FfiConverterSequenceUInt8.lower(bytes), $0 + ) + }) } public func actorId() -> ActorId { try! FfiConverterTypeActorId.lift( try! rustCall { - automerge_9b9_Doc_actor_id( + uniffi_automerge_fn_method_doc_actor_id( self.pointer, $0 ) @@ -507,7 +509,7 @@ public class Doc: DocProtocol { public func setActor(actor: ActorId) { try! rustCall { - automerge_9b9_Doc_set_actor( + uniffi_automerge_fn_method_doc_set_actor( self.pointer, FfiConverterTypeActorId.lower(`actor`), @@ -520,7 +522,7 @@ public class Doc: DocProtocol { try! FfiConverterTypeDoc.lift( try! rustCall { - automerge_9b9_Doc_fork( + uniffi_automerge_fn_method_doc_fork( self.pointer, $0 ) @@ -530,27 +532,26 @@ public class Doc: DocProtocol { public func forkAt(heads: [ChangeHash]) throws -> Doc { try FfiConverterTypeDoc.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_fork_at( - self.pointer, + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_fork_at( + self.pointer, - FfiConverterSequenceTypeChangeHash.lower(heads), - $0 - ) - } + FfiConverterSequenceTypeChangeHash.lower(heads), + $0 + ) + } ) } public func putInMap(obj: ObjId, key: String, value: ScalarValue) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_put_in_map( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_put_in_map( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterString.lower(key), - FfiConverterTypeScalarValue.lower(value), $0 ) @@ -559,31 +560,28 @@ public class Doc: DocProtocol { public func putObjectInMap(obj: ObjId, key: String, objType: ObjType) throws -> ObjId { try FfiConverterTypeObjId.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_put_object_in_map( - self.pointer, - - FfiConverterTypeObjId.lower(obj), - - FfiConverterString.lower(key), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_put_object_in_map( + self.pointer, - FfiConverterTypeObjType.lower(objType), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterString.lower(key), + FfiConverterTypeObjType.lower(objType), + $0 + ) + } ) } public func putInList(obj: ObjId, index: UInt64, value: ScalarValue) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_put_in_list( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_put_in_list( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterUInt64.lower(index), - FfiConverterTypeScalarValue.lower(value), $0 ) @@ -592,31 +590,28 @@ public class Doc: DocProtocol { public func putObjectInList(obj: ObjId, index: UInt64, objType: ObjType) throws -> ObjId { try FfiConverterTypeObjId.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_put_object_in_list( - self.pointer, - - FfiConverterTypeObjId.lower(obj), - - FfiConverterUInt64.lower(index), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_put_object_in_list( + self.pointer, - FfiConverterTypeObjType.lower(objType), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterUInt64.lower(index), + FfiConverterTypeObjType.lower(objType), + $0 + ) + } ) } public func insertInList(obj: ObjId, index: UInt64, value: ScalarValue) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_insert_in_list( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_insert_in_list( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterUInt64.lower(index), - FfiConverterTypeScalarValue.lower(value), $0 ) @@ -625,65 +620,110 @@ public class Doc: DocProtocol { public func insertObjectInList(obj: ObjId, index: UInt64, objType: ObjType) throws -> ObjId { try FfiConverterTypeObjId.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_insert_object_in_list( - self.pointer, + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_insert_object_in_list( + self.pointer, - FfiConverterTypeObjId.lower(obj), + FfiConverterTypeObjId.lower(obj), + FfiConverterUInt64.lower(index), + FfiConverterTypeObjType.lower(objType), + $0 + ) + } + ) + } - FfiConverterUInt64.lower(index), + public func spliceText(obj: ObjId, start: UInt64, delete: Int64, chars: String) throws { + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_splice_text( + self.pointer, - FfiConverterTypeObjType.lower(objType), + FfiConverterTypeObjId.lower(obj), + FfiConverterUInt64.lower(start), + FfiConverterInt64.lower(delete), + FfiConverterString.lower(chars), $0 ) } - ) } - public func spliceText(obj: ObjId, start: UInt64, delete: UInt64, chars: String) throws { + public func splice(obj: ObjId, start: UInt64, delete: Int64, values: [ScalarValue]) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_splice_text( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_splice( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterUInt64.lower(start), - - FfiConverterUInt64.lower(delete), - - FfiConverterString.lower(chars), + FfiConverterInt64.lower(delete), + FfiConverterSequenceTypeScalarValue.lower(values), $0 ) } } - public func splice(obj: ObjId, start: UInt64, delete: UInt64, values: [ScalarValue]) throws { + public func mark( + obj: ObjId, + start: UInt64, + end: UInt64, + expand: ExpandMark, + name: String, + value: ScalarValue + ) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_splice( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_mark( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterUInt64.lower(start), - - FfiConverterUInt64.lower(delete), - - FfiConverterSequenceTypeScalarValue.lower(values), + FfiConverterUInt64.lower(end), + FfiConverterTypeExpandMark.lower(expand), + FfiConverterString.lower(name), + FfiConverterTypeScalarValue.lower(value), $0 ) } } + public func marks(obj: ObjId) throws -> [Mark] { + try FfiConverterSequenceTypeMark.lift( + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_marks( + self.pointer, + + FfiConverterTypeObjId.lower(obj), + $0 + ) + } + ) + } + + public func marksAt(obj: ObjId, heads: [ChangeHash]) throws -> [Mark] { + try FfiConverterSequenceTypeMark.lift( + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_marks_at( + self.pointer, + + FfiConverterTypeObjId.lower(obj), + FfiConverterSequenceTypeChangeHash.lower(heads), + $0 + ) + } + ) + } + public func deleteInMap(obj: ObjId, key: String) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_delete_in_map( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_delete_in_map( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterString.lower(key), $0 ) @@ -692,12 +732,11 @@ public class Doc: DocProtocol { public func deleteInList(obj: ObjId, index: UInt64) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_delete_in_list( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_delete_in_list( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterUInt64.lower(index), $0 ) @@ -706,14 +745,12 @@ public class Doc: DocProtocol { public func incrementInMap(obj: ObjId, key: String, by: Int64) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_increment_in_map( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_increment_in_map( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterString.lower(key), - FfiConverterInt64.lower(by), $0 ) @@ -722,14 +759,12 @@ public class Doc: DocProtocol { public func incrementInList(obj: ObjId, index: UInt64, by: Int64) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_increment_in_list( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_increment_in_list( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterUInt64.lower(index), - FfiConverterInt64.lower(by), $0 ) @@ -738,157 +773,154 @@ public class Doc: DocProtocol { public func getInMap(obj: ObjId, key: String) throws -> Value? { try FfiConverterOptionTypeValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_get_in_map( - self.pointer, - - FfiConverterTypeObjId.lower(obj), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_get_in_map( + self.pointer, - FfiConverterString.lower(key), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterString.lower(key), + $0 + ) + } ) } public func getInList(obj: ObjId, index: UInt64) throws -> Value? { try FfiConverterOptionTypeValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_get_in_list( - self.pointer, - - FfiConverterTypeObjId.lower(obj), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_get_in_list( + self.pointer, - FfiConverterUInt64.lower(index), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterUInt64.lower(index), + $0 + ) + } ) } public func getAtInMap(obj: ObjId, key: String, heads: [ChangeHash]) throws -> Value? { try FfiConverterOptionTypeValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_get_at_in_map( - self.pointer, - - FfiConverterTypeObjId.lower(obj), - - FfiConverterString.lower(key), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_get_at_in_map( + self.pointer, - FfiConverterSequenceTypeChangeHash.lower(heads), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterString.lower(key), + FfiConverterSequenceTypeChangeHash.lower(heads), + $0 + ) + } ) } public func getAtInList(obj: ObjId, index: UInt64, heads: [ChangeHash]) throws -> Value? { try FfiConverterOptionTypeValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_get_at_in_list( - self.pointer, - - FfiConverterTypeObjId.lower(obj), - - FfiConverterUInt64.lower(index), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_get_at_in_list( + self.pointer, - FfiConverterSequenceTypeChangeHash.lower(heads), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterUInt64.lower(index), + FfiConverterSequenceTypeChangeHash.lower(heads), + $0 + ) + } ) } public func getAllInMap(obj: ObjId, key: String) throws -> [Value] { try FfiConverterSequenceTypeValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_get_all_in_map( - self.pointer, - - FfiConverterTypeObjId.lower(obj), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_get_all_in_map( + self.pointer, - FfiConverterString.lower(key), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterString.lower(key), + $0 + ) + } ) } public func getAllInList(obj: ObjId, index: UInt64) throws -> [Value] { try FfiConverterSequenceTypeValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_get_all_in_list( - self.pointer, - - FfiConverterTypeObjId.lower(obj), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_get_all_in_list( + self.pointer, - FfiConverterUInt64.lower(index), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterUInt64.lower(index), + $0 + ) + } ) } public func getAllAtInMap(obj: ObjId, key: String, heads: [ChangeHash]) throws -> [Value] { try FfiConverterSequenceTypeValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_get_all_at_in_map( - self.pointer, - - FfiConverterTypeObjId.lower(obj), - - FfiConverterString.lower(key), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_get_all_at_in_map( + self.pointer, - FfiConverterSequenceTypeChangeHash.lower(heads), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterString.lower(key), + FfiConverterSequenceTypeChangeHash.lower(heads), + $0 + ) + } ) } public func getAllAtInList(obj: ObjId, index: UInt64, heads: [ChangeHash]) throws -> [Value] { try FfiConverterSequenceTypeValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_get_all_at_in_list( - self.pointer, - - FfiConverterTypeObjId.lower(obj), - - FfiConverterUInt64.lower(index), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_get_all_at_in_list( + self.pointer, - FfiConverterSequenceTypeChangeHash.lower(heads), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterUInt64.lower(index), + FfiConverterSequenceTypeChangeHash.lower(heads), + $0 + ) + } ) } public func text(obj: ObjId) throws -> String { try FfiConverterString.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_text( - self.pointer, + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_text( + self.pointer, - FfiConverterTypeObjId.lower(obj), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + $0 + ) + } ) } public func textAt(obj: ObjId, heads: [ChangeHash]) throws -> String { try FfiConverterString.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_text_at( - self.pointer, - - FfiConverterTypeObjId.lower(obj), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_text_at( + self.pointer, - FfiConverterSequenceTypeChangeHash.lower(heads), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterSequenceTypeChangeHash.lower(heads), + $0 + ) + } ) } @@ -896,7 +928,7 @@ public class Doc: DocProtocol { try! FfiConverterSequenceString.lift( try! rustCall { - automerge_9b9_Doc_map_keys( + uniffi_automerge_fn_method_doc_map_keys( self.pointer, FfiConverterTypeObjId.lower(obj), @@ -910,11 +942,10 @@ public class Doc: DocProtocol { try! FfiConverterSequenceString.lift( try! rustCall { - automerge_9b9_Doc_map_keys_at( + uniffi_automerge_fn_method_doc_map_keys_at( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterSequenceTypeChangeHash.lower(heads), $0 ) @@ -924,57 +955,59 @@ public class Doc: DocProtocol { public func mapEntries(obj: ObjId) throws -> [KeyValue] { try FfiConverterSequenceTypeKeyValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_map_entries( - self.pointer, + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_map_entries( + self.pointer, - FfiConverterTypeObjId.lower(obj), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + $0 + ) + } ) } public func mapEntriesAt(obj: ObjId, heads: [ChangeHash]) throws -> [KeyValue] { try FfiConverterSequenceTypeKeyValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_map_entries_at( - self.pointer, - - FfiConverterTypeObjId.lower(obj), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_map_entries_at( + self.pointer, - FfiConverterSequenceTypeChangeHash.lower(heads), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterSequenceTypeChangeHash.lower(heads), + $0 + ) + } ) } public func values(obj: ObjId) throws -> [Value] { try FfiConverterSequenceTypeValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_values( - self.pointer, + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_values( + self.pointer, - FfiConverterTypeObjId.lower(obj), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + $0 + ) + } ) } public func valuesAt(obj: ObjId, heads: [ChangeHash]) throws -> [Value] { try FfiConverterSequenceTypeValue.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_values_at( - self.pointer, - - FfiConverterTypeObjId.lower(obj), + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_values_at( + self.pointer, - FfiConverterSequenceTypeChangeHash.lower(heads), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + FfiConverterSequenceTypeChangeHash.lower(heads), + $0 + ) + } ) } @@ -982,7 +1015,7 @@ public class Doc: DocProtocol { try! FfiConverterUInt64.lift( try! rustCall { - automerge_9b9_Doc_length( + uniffi_automerge_fn_method_doc_length( self.pointer, FfiConverterTypeObjId.lower(obj), @@ -996,11 +1029,10 @@ public class Doc: DocProtocol { try! FfiConverterUInt64.lift( try! rustCall { - automerge_9b9_Doc_length_at( + uniffi_automerge_fn_method_doc_length_at( self.pointer, FfiConverterTypeObjId.lower(obj), - FfiConverterSequenceTypeChangeHash.lower(heads), $0 ) @@ -1012,7 +1044,7 @@ public class Doc: DocProtocol { try! FfiConverterTypeObjType.lift( try! rustCall { - automerge_9b9_Doc_object_type( + uniffi_automerge_fn_method_doc_object_type( self.pointer, FfiConverterTypeObjId.lower(obj), @@ -1024,14 +1056,15 @@ public class Doc: DocProtocol { public func path(obj: ObjId) throws -> [PathElement] { try FfiConverterSequenceTypePathElement.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_path( - self.pointer, + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_path( + self.pointer, - FfiConverterTypeObjId.lower(obj), - $0 - ) - } + FfiConverterTypeObjId.lower(obj), + $0 + ) + } ) } @@ -1039,7 +1072,7 @@ public class Doc: DocProtocol { try! FfiConverterSequenceTypeChangeHash.lift( try! rustCall { - automerge_9b9_Doc_heads( + uniffi_automerge_fn_method_doc_heads( self.pointer, $0 ) @@ -1051,7 +1084,7 @@ public class Doc: DocProtocol { try! FfiConverterSequenceUInt8.lift( try! rustCall { - automerge_9b9_Doc_save( + uniffi_automerge_fn_method_doc_save( self.pointer, $0 ) @@ -1061,8 +1094,8 @@ public class Doc: DocProtocol { public func merge(other: Doc) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_merge( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_merge( self.pointer, FfiConverterTypeDoc.lower(other), @@ -1073,14 +1106,15 @@ public class Doc: DocProtocol { public func mergeWithPatches(other: Doc) throws -> [Patch] { try FfiConverterSequenceTypePatch.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_merge_with_patches( - self.pointer, + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_merge_with_patches( + self.pointer, - FfiConverterTypeDoc.lower(other), - $0 - ) - } + FfiConverterTypeDoc.lower(other), + $0 + ) + } ) } @@ -1088,7 +1122,7 @@ public class Doc: DocProtocol { try! FfiConverterOptionSequenceUInt8.lift( try! rustCall { - automerge_9b9_Doc_generate_sync_message( + uniffi_automerge_fn_method_doc_generate_sync_message( self.pointer, FfiConverterTypeSyncState.lower(state), @@ -1100,12 +1134,11 @@ public class Doc: DocProtocol { public func receiveSyncMessage(state: SyncState, msg: [UInt8]) throws { try - rustCallWithError(FfiConverterTypeReceiveSyncError.self) { - automerge_9b9_Doc_receive_sync_message( + rustCallWithError(FfiConverterTypeReceiveSyncError.lift) { + uniffi_automerge_fn_method_doc_receive_sync_message( self.pointer, FfiConverterTypeSyncState.lower(state), - FfiConverterSequenceUInt8.lower(msg), $0 ) @@ -1114,16 +1147,16 @@ public class Doc: DocProtocol { public func receiveSyncMessageWithPatches(state: SyncState, msg: [UInt8]) throws -> [Patch] { try FfiConverterSequenceTypePatch.lift( - rustCallWithError(FfiConverterTypeReceiveSyncError.self) { - automerge_9b9_Doc_receive_sync_message_with_patches( - self.pointer, - - FfiConverterTypeSyncState.lower(state), + try + rustCallWithError(FfiConverterTypeReceiveSyncError.lift) { + uniffi_automerge_fn_method_doc_receive_sync_message_with_patches( + self.pointer, - FfiConverterSequenceUInt8.lower(msg), - $0 - ) - } + FfiConverterTypeSyncState.lower(state), + FfiConverterSequenceUInt8.lower(msg), + $0 + ) + } ) } @@ -1131,7 +1164,7 @@ public class Doc: DocProtocol { try! FfiConverterSequenceUInt8.lift( try! rustCall { - automerge_9b9_Doc_encode_new_changes( + uniffi_automerge_fn_method_doc_encode_new_changes( self.pointer, $0 ) @@ -1141,21 +1174,22 @@ public class Doc: DocProtocol { public func encodeChangesSince(heads: [ChangeHash]) throws -> [UInt8] { try FfiConverterSequenceUInt8.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_encode_changes_since( - self.pointer, + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_encode_changes_since( + self.pointer, - FfiConverterSequenceTypeChangeHash.lower(heads), - $0 - ) - } + FfiConverterSequenceTypeChangeHash.lower(heads), + $0 + ) + } ) } public func applyEncodedChanges(changes: [UInt8]) throws { try - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_apply_encoded_changes( + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_apply_encoded_changes( self.pointer, FfiConverterSequenceUInt8.lower(changes), @@ -1166,14 +1200,15 @@ public class Doc: DocProtocol { public func applyEncodedChangesWithPatches(changes: [UInt8]) throws -> [Patch] { try FfiConverterSequenceTypePatch.lift( - rustCallWithError(FfiConverterTypeDocError.self) { - automerge_9b9_Doc_apply_encoded_changes_with_patches( - self.pointer, + try + rustCallWithError(FfiConverterTypeDocError.lift) { + uniffi_automerge_fn_method_doc_apply_encoded_changes_with_patches( + self.pointer, - FfiConverterSequenceUInt8.lower(changes), - $0 - ) - } + FfiConverterSequenceUInt8.lower(changes), + $0 + ) + } ) } } @@ -1208,6 +1243,14 @@ public struct FfiConverterTypeDoc: FfiConverter { } } +public func FfiConverterTypeDoc_lift(_ pointer: UnsafeMutableRawPointer) throws -> Doc { + try FfiConverterTypeDoc.lift(pointer) +} + +public func FfiConverterTypeDoc_lower(_ value: Doc) -> UnsafeMutableRawPointer { + FfiConverterTypeDoc.lower(value) +} + public protocol SyncStateProtocol { func encode() -> [UInt8] func reset() @@ -1225,36 +1268,28 @@ public class SyncState: SyncStateProtocol { } public convenience init() { - self.init( - unsafeFromRawPointer: try! - - rustCall { - automerge_9b9_SyncState_new($0) - } - ) + self.init(unsafeFromRawPointer: try! rustCall { + uniffi_automerge_fn_constructor_syncstate_new($0) + }) } deinit { - try! rustCall { ffi_automerge_9b9_SyncState_object_free(pointer, $0) } + try! rustCall { uniffi_automerge_fn_free_syncstate(pointer, $0) } } public static func decode(bytes: [UInt8]) throws -> SyncState { - try SyncState( - unsafeFromRawPointer: - - rustCallWithError(FfiConverterTypeDecodeSyncStateError.self) { - automerge_9b9_SyncState_decode( - FfiConverterSequenceUInt8.lower(bytes), $0 - ) - } - ) + SyncState(unsafeFromRawPointer: try rustCallWithError(FfiConverterTypeDecodeSyncStateError.lift) { + uniffi_automerge_fn_constructor_syncstate_decode( + FfiConverterSequenceUInt8.lower(bytes), $0 + ) + }) } public func encode() -> [UInt8] { try! FfiConverterSequenceUInt8.lift( try! rustCall { - automerge_9b9_SyncState_encode( + uniffi_automerge_fn_method_syncstate_encode( self.pointer, $0 ) @@ -1265,7 +1300,7 @@ public class SyncState: SyncStateProtocol { public func reset() { try! rustCall { - automerge_9b9_SyncState_reset( + uniffi_automerge_fn_method_syncstate_reset( self.pointer, $0 ) @@ -1276,7 +1311,7 @@ public class SyncState: SyncStateProtocol { try! FfiConverterOptionSequenceTypeChangeHash.lift( try! rustCall { - automerge_9b9_SyncState_their_heads( + uniffi_automerge_fn_method_syncstate_their_heads( self.pointer, $0 ) @@ -1315,6 +1350,14 @@ public struct FfiConverterTypeSyncState: FfiConverter { } } +public func FfiConverterTypeSyncState_lift(_ pointer: UnsafeMutableRawPointer) throws -> SyncState { + try FfiConverterTypeSyncState.lift(pointer) +} + +public func FfiConverterTypeSyncState_lower(_ value: SyncState) -> UnsafeMutableRawPointer { + FfiConverterTypeSyncState.lower(value) +} + public struct KeyValue { public var key: String public var value: Value @@ -1366,6 +1409,73 @@ public func FfiConverterTypeKeyValue_lower(_ value: KeyValue) -> RustBuffer { FfiConverterTypeKeyValue.lower(value) } +public struct Mark { + public var start: UInt64 + public var end: UInt64 + public var name: String + public var value: Value + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(start: UInt64, end: UInt64, name: String, value: Value) { + self.start = start + self.end = end + self.name = name + self.value = value + } +} + +extension Mark: Equatable, Hashable { + public static func == (lhs: Mark, rhs: Mark) -> Bool { + if lhs.start != rhs.start { + return false + } + if lhs.end != rhs.end { + return false + } + if lhs.name != rhs.name { + return false + } + if lhs.value != rhs.value { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(start) + hasher.combine(end) + hasher.combine(name) + hasher.combine(value) + } +} + +public struct FfiConverterTypeMark: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Mark { + try Mark( + start: FfiConverterUInt64.read(from: &buf), + end: FfiConverterUInt64.read(from: &buf), + name: FfiConverterString.read(from: &buf), + value: FfiConverterTypeValue.read(from: &buf) + ) + } + + public static func write(_ value: Mark, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.start, into: &buf) + FfiConverterUInt64.write(value.end, into: &buf) + FfiConverterString.write(value.name, into: &buf) + FfiConverterTypeValue.write(value.value, into: &buf) + } +} + +public func FfiConverterTypeMark_lift(_ buf: RustBuffer) throws -> Mark { + try FfiConverterTypeMark.lift(buf) +} + +public func FfiConverterTypeMark_lower(_ value: Mark) -> RustBuffer { + FfiConverterTypeMark.lower(value) +} + public struct Patch { public var path: [PathElement] public var action: PatchAction @@ -1468,6 +1578,174 @@ public func FfiConverterTypePathElement_lower(_ value: PathElement) -> RustBuffe FfiConverterTypePathElement.lower(value) } +public enum DecodeSyncStateError { + // Simple error enums only carry a message + case Internal(message: String) + + fileprivate static func uniffiErrorHandler(_ error: RustBuffer) throws -> Error { + try FfiConverterTypeDecodeSyncStateError.lift(error) + } +} + +public struct FfiConverterTypeDecodeSyncStateError: FfiConverterRustBuffer { + typealias SwiftType = DecodeSyncStateError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DecodeSyncStateError { + let variant: Int32 = try readInt(&buf) + switch variant { + case 1: return .Internal( + message: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: DecodeSyncStateError, into buf: inout [UInt8]) { + switch value { + case let .Internal(message): + writeInt(&buf, Int32(1)) + } + } +} + +extension DecodeSyncStateError: Equatable, Hashable {} + +extension DecodeSyncStateError: Error {} + +public enum DocError { + // Simple error enums only carry a message + case WrongObjectType(message: String) + + // Simple error enums only carry a message + case Internal(message: String) + + fileprivate static func uniffiErrorHandler(_ error: RustBuffer) throws -> Error { + try FfiConverterTypeDocError.lift(error) + } +} + +public struct FfiConverterTypeDocError: FfiConverterRustBuffer { + typealias SwiftType = DocError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DocError { + let variant: Int32 = try readInt(&buf) + switch variant { + case 1: return .WrongObjectType( + message: try FfiConverterString.read(from: &buf) + ) + + case 2: return .Internal( + message: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: DocError, into buf: inout [UInt8]) { + switch value { + case let .WrongObjectType(message): + writeInt(&buf, Int32(1)) + case let .Internal(message): + writeInt(&buf, Int32(2)) + } + } +} + +extension DocError: Equatable, Hashable {} + +extension DocError: Error {} + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +public enum ExpandMark { + case before + case after + case none + case both +} + +public struct FfiConverterTypeExpandMark: FfiConverterRustBuffer { + typealias SwiftType = ExpandMark + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ExpandMark { + let variant: Int32 = try readInt(&buf) + switch variant { + case 1: return .before + + case 2: return .after + + case 3: return .none + + case 4: return .both + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: ExpandMark, into buf: inout [UInt8]) { + switch value { + case .before: + writeInt(&buf, Int32(1)) + + case .after: + writeInt(&buf, Int32(2)) + + case .none: + writeInt(&buf, Int32(3)) + + case .both: + writeInt(&buf, Int32(4)) + } + } +} + +public func FfiConverterTypeExpandMark_lift(_ buf: RustBuffer) throws -> ExpandMark { + try FfiConverterTypeExpandMark.lift(buf) +} + +public func FfiConverterTypeExpandMark_lower(_ value: ExpandMark) -> RustBuffer { + FfiConverterTypeExpandMark.lower(value) +} + +extension ExpandMark: Equatable, Hashable {} + +public enum LoadError { + // Simple error enums only carry a message + case Internal(message: String) + + fileprivate static func uniffiErrorHandler(_ error: RustBuffer) throws -> Error { + try FfiConverterTypeLoadError.lift(error) + } +} + +public struct FfiConverterTypeLoadError: FfiConverterRustBuffer { + typealias SwiftType = LoadError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LoadError { + let variant: Int32 = try readInt(&buf) + switch variant { + case 1: return .Internal( + message: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: LoadError, into buf: inout [UInt8]) { + switch value { + case let .Internal(message): + writeInt(&buf, Int32(1)) + } + } +} + +extension LoadError: Equatable, Hashable {} + +extension LoadError: Error {} + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. public enum ObjType { @@ -1520,11 +1798,13 @@ extension ObjType: Equatable, Hashable {} // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. public enum PatchAction { case put(obj: ObjId, prop: Prop, value: Value) - case insert(obj: ObjId, index: UInt64, values: [Value]) - case spliceText(obj: ObjId, index: UInt64, value: String) + case insert(obj: ObjId, index: UInt64, values: [Value], marks: [String: Value]) + case spliceText(obj: ObjId, index: UInt64, value: String, marks: [String: Value]) case increment(obj: ObjId, prop: Prop, value: Int64) + case conflict(obj: ObjId, prop: Prop) case deleteMap(obj: ObjId, key: String) case deleteSeq(obj: ObjId, index: UInt64, length: UInt64) + case marks(obj: ObjId, marks: [Mark]) } public struct FfiConverterTypePatchAction: FfiConverterRustBuffer { @@ -1533,39 +1813,51 @@ public struct FfiConverterTypePatchAction: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PatchAction { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return try .put( - obj: FfiConverterTypeObjId.read(from: &buf), - prop: FfiConverterTypeProp.read(from: &buf), - value: FfiConverterTypeValue.read(from: &buf) + case 1: return .put( + obj: try FfiConverterTypeObjId.read(from: &buf), + prop: try FfiConverterTypeProp.read(from: &buf), + value: try FfiConverterTypeValue.read(from: &buf) ) - case 2: return try .insert( - obj: FfiConverterTypeObjId.read(from: &buf), - index: FfiConverterUInt64.read(from: &buf), - values: FfiConverterSequenceTypeValue.read(from: &buf) + case 2: return .insert( + obj: try FfiConverterTypeObjId.read(from: &buf), + index: try FfiConverterUInt64.read(from: &buf), + values: try FfiConverterSequenceTypeValue.read(from: &buf), + marks: try FfiConverterDictionaryStringTypeValue.read(from: &buf) ) - case 3: return try .spliceText( - obj: FfiConverterTypeObjId.read(from: &buf), - index: FfiConverterUInt64.read(from: &buf), - value: FfiConverterString.read(from: &buf) + case 3: return .spliceText( + obj: try FfiConverterTypeObjId.read(from: &buf), + index: try FfiConverterUInt64.read(from: &buf), + value: try FfiConverterString.read(from: &buf), + marks: try FfiConverterDictionaryStringTypeValue.read(from: &buf) ) - case 4: return try .increment( - obj: FfiConverterTypeObjId.read(from: &buf), - prop: FfiConverterTypeProp.read(from: &buf), - value: FfiConverterInt64.read(from: &buf) + case 4: return .increment( + obj: try FfiConverterTypeObjId.read(from: &buf), + prop: try FfiConverterTypeProp.read(from: &buf), + value: try FfiConverterInt64.read(from: &buf) ) - case 5: return try .deleteMap( - obj: FfiConverterTypeObjId.read(from: &buf), - key: FfiConverterString.read(from: &buf) + case 5: return .conflict( + obj: try FfiConverterTypeObjId.read(from: &buf), + prop: try FfiConverterTypeProp.read(from: &buf) ) - case 6: return try .deleteSeq( - obj: FfiConverterTypeObjId.read(from: &buf), - index: FfiConverterUInt64.read(from: &buf), - length: FfiConverterUInt64.read(from: &buf) + case 6: return .deleteMap( + obj: try FfiConverterTypeObjId.read(from: &buf), + key: try FfiConverterString.read(from: &buf) + ) + + case 7: return .deleteSeq( + obj: try FfiConverterTypeObjId.read(from: &buf), + index: try FfiConverterUInt64.read(from: &buf), + length: try FfiConverterUInt64.read(from: &buf) + ) + + case 8: return .marks( + obj: try FfiConverterTypeObjId.read(from: &buf), + marks: try FfiConverterSequenceTypeMark.read(from: &buf) ) default: throw UniffiInternalError.unexpectedEnumCase @@ -1580,17 +1872,19 @@ public struct FfiConverterTypePatchAction: FfiConverterRustBuffer { FfiConverterTypeProp.write(prop, into: &buf) FfiConverterTypeValue.write(value, into: &buf) - case let .insert(obj, index, values): + case let .insert(obj, index, values, marks): writeInt(&buf, Int32(2)) FfiConverterTypeObjId.write(obj, into: &buf) FfiConverterUInt64.write(index, into: &buf) FfiConverterSequenceTypeValue.write(values, into: &buf) + FfiConverterDictionaryStringTypeValue.write(marks, into: &buf) - case let .spliceText(obj, index, value): + case let .spliceText(obj, index, value, marks): writeInt(&buf, Int32(3)) FfiConverterTypeObjId.write(obj, into: &buf) FfiConverterUInt64.write(index, into: &buf) FfiConverterString.write(value, into: &buf) + FfiConverterDictionaryStringTypeValue.write(marks, into: &buf) case let .increment(obj, prop, value): writeInt(&buf, Int32(4)) @@ -1598,16 +1892,26 @@ public struct FfiConverterTypePatchAction: FfiConverterRustBuffer { FfiConverterTypeProp.write(prop, into: &buf) FfiConverterInt64.write(value, into: &buf) - case let .deleteMap(obj, key): + case let .conflict(obj, prop): writeInt(&buf, Int32(5)) FfiConverterTypeObjId.write(obj, into: &buf) + FfiConverterTypeProp.write(prop, into: &buf) + + case let .deleteMap(obj, key): + writeInt(&buf, Int32(6)) + FfiConverterTypeObjId.write(obj, into: &buf) FfiConverterString.write(key, into: &buf) case let .deleteSeq(obj, index, length): - writeInt(&buf, Int32(6)) + writeInt(&buf, Int32(7)) FfiConverterTypeObjId.write(obj, into: &buf) FfiConverterUInt64.write(index, into: &buf) FfiConverterUInt64.write(length, into: &buf) + + case let .marks(obj, marks): + writeInt(&buf, Int32(8)) + FfiConverterTypeObjId.write(obj, into: &buf) + FfiConverterSequenceTypeMark.write(marks, into: &buf) } } } @@ -1629,46 +1933,90 @@ public enum Prop { case index(value: UInt64) } -public struct FfiConverterTypeProp: FfiConverterRustBuffer { - typealias SwiftType = Prop +public struct FfiConverterTypeProp: FfiConverterRustBuffer { + typealias SwiftType = Prop + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Prop { + let variant: Int32 = try readInt(&buf) + switch variant { + case 1: return .key( + value: try FfiConverterString.read(from: &buf) + ) + + case 2: return .index( + value: try FfiConverterUInt64.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: Prop, into buf: inout [UInt8]) { + switch value { + case let .key(value): + writeInt(&buf, Int32(1)) + FfiConverterString.write(value, into: &buf) + + case let .index(value): + writeInt(&buf, Int32(2)) + FfiConverterUInt64.write(value, into: &buf) + } + } +} + +public func FfiConverterTypeProp_lift(_ buf: RustBuffer) throws -> Prop { + try FfiConverterTypeProp.lift(buf) +} + +public func FfiConverterTypeProp_lower(_ value: Prop) -> RustBuffer { + FfiConverterTypeProp.lower(value) +} + +extension Prop: Equatable, Hashable {} + +public enum ReceiveSyncError { + // Simple error enums only carry a message + case Internal(message: String) + + // Simple error enums only carry a message + case InvalidMessage(message: String) + + fileprivate static func uniffiErrorHandler(_ error: RustBuffer) throws -> Error { + try FfiConverterTypeReceiveSyncError.lift(error) + } +} + +public struct FfiConverterTypeReceiveSyncError: FfiConverterRustBuffer { + typealias SwiftType = ReceiveSyncError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Prop { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ReceiveSyncError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return try .key( - value: FfiConverterString.read(from: &buf) + case 1: return .Internal( + message: try FfiConverterString.read(from: &buf) ) - case 2: return try .index( - value: FfiConverterUInt64.read(from: &buf) + case 2: return .InvalidMessage( + message: try FfiConverterString.read(from: &buf) ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: Prop, into buf: inout [UInt8]) { + public static func write(_ value: ReceiveSyncError, into buf: inout [UInt8]) { switch value { - case let .key(value): + case let .Internal(message): writeInt(&buf, Int32(1)) - FfiConverterString.write(value, into: &buf) - - case let .index(value): + case let .InvalidMessage(message): writeInt(&buf, Int32(2)) - FfiConverterUInt64.write(value, into: &buf) } } } -public func FfiConverterTypeProp_lift(_ buf: RustBuffer) throws -> Prop { - try FfiConverterTypeProp.lift(buf) -} - -public func FfiConverterTypeProp_lower(_ value: Prop) -> RustBuffer { - FfiConverterTypeProp.lower(value) -} +extension ReceiveSyncError: Equatable, Hashable {} -extension Prop: Equatable, Hashable {} +extension ReceiveSyncError: Error {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. @@ -1691,41 +2039,41 @@ public struct FfiConverterTypeScalarValue: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ScalarValue { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return try .bytes( - value: FfiConverterSequenceUInt8.read(from: &buf) + case 1: return .bytes( + value: try FfiConverterSequenceUInt8.read(from: &buf) ) - case 2: return try .string( - value: FfiConverterString.read(from: &buf) + case 2: return .string( + value: try FfiConverterString.read(from: &buf) ) - case 3: return try .uint( - value: FfiConverterUInt64.read(from: &buf) + case 3: return .uint( + value: try FfiConverterUInt64.read(from: &buf) ) - case 4: return try .int( - value: FfiConverterInt64.read(from: &buf) + case 4: return .int( + value: try FfiConverterInt64.read(from: &buf) ) - case 5: return try .f64( - value: FfiConverterDouble.read(from: &buf) + case 5: return .f64( + value: try FfiConverterDouble.read(from: &buf) ) - case 6: return try .counter( - value: FfiConverterInt64.read(from: &buf) + case 6: return .counter( + value: try FfiConverterInt64.read(from: &buf) ) - case 7: return try .timestamp( - value: FfiConverterInt64.read(from: &buf) + case 7: return .timestamp( + value: try FfiConverterInt64.read(from: &buf) ) - case 8: return try .boolean( - value: FfiConverterBool.read(from: &buf) + case 8: return .boolean( + value: try FfiConverterBool.read(from: &buf) ) - case 9: return try .unknown( - typeCode: FfiConverterUInt8.read(from: &buf), - data: FfiConverterSequenceUInt8.read(from: &buf) + case 9: return .unknown( + typeCode: try FfiConverterUInt8.read(from: &buf), + data: try FfiConverterSequenceUInt8.read(from: &buf) ) case 10: return .null @@ -1802,13 +2150,13 @@ public struct FfiConverterTypeValue: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Value { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return try .object( - typ: FfiConverterTypeObjType.read(from: &buf), - id: FfiConverterTypeObjId.read(from: &buf) + case 1: return .object( + typ: try FfiConverterTypeObjType.read(from: &buf), + id: try FfiConverterTypeObjId.read(from: &buf) ) - case 2: return try .scalar( - value: FfiConverterTypeScalarValue.read(from: &buf) + case 2: return .scalar( + value: try FfiConverterTypeScalarValue.read(from: &buf) ) default: throw UniffiInternalError.unexpectedEnumCase @@ -1839,154 +2187,6 @@ public func FfiConverterTypeValue_lower(_ value: Value) -> RustBuffer { extension Value: Equatable, Hashable {} -public enum DecodeSyncStateError { - // Simple error enums only carry a message - case Internal(message: String) -} - -public struct FfiConverterTypeDecodeSyncStateError: FfiConverterRustBuffer { - typealias SwiftType = DecodeSyncStateError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DecodeSyncStateError { - let variant: Int32 = try readInt(&buf) - switch variant { - case 1: return try .Internal( - message: FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: DecodeSyncStateError, into buf: inout [UInt8]) { - switch value { - case let .Internal(message): - writeInt(&buf, Int32(1)) - FfiConverterString.write(message, into: &buf) - } - } -} - -extension DecodeSyncStateError: Equatable, Hashable {} - -extension DecodeSyncStateError: Error {} - -public enum DocError { - // Simple error enums only carry a message - case WrongObjectType(message: String) - - // Simple error enums only carry a message - case Internal(message: String) -} - -public struct FfiConverterTypeDocError: FfiConverterRustBuffer { - typealias SwiftType = DocError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DocError { - let variant: Int32 = try readInt(&buf) - switch variant { - case 1: return try .WrongObjectType( - message: FfiConverterString.read(from: &buf) - ) - - case 2: return try .Internal( - message: FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: DocError, into buf: inout [UInt8]) { - switch value { - case let .WrongObjectType(message): - writeInt(&buf, Int32(1)) - FfiConverterString.write(message, into: &buf) - case let .Internal(message): - writeInt(&buf, Int32(2)) - FfiConverterString.write(message, into: &buf) - } - } -} - -extension DocError: Equatable, Hashable {} - -extension DocError: Error {} - -public enum LoadError { - // Simple error enums only carry a message - case Internal(message: String) -} - -public struct FfiConverterTypeLoadError: FfiConverterRustBuffer { - typealias SwiftType = LoadError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LoadError { - let variant: Int32 = try readInt(&buf) - switch variant { - case 1: return try .Internal( - message: FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: LoadError, into buf: inout [UInt8]) { - switch value { - case let .Internal(message): - writeInt(&buf, Int32(1)) - FfiConverterString.write(message, into: &buf) - } - } -} - -extension LoadError: Equatable, Hashable {} - -extension LoadError: Error {} - -public enum ReceiveSyncError { - // Simple error enums only carry a message - case Internal(message: String) - - // Simple error enums only carry a message - case InvalidMessage(message: String) -} - -public struct FfiConverterTypeReceiveSyncError: FfiConverterRustBuffer { - typealias SwiftType = ReceiveSyncError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ReceiveSyncError { - let variant: Int32 = try readInt(&buf) - switch variant { - case 1: return try .Internal( - message: FfiConverterString.read(from: &buf) - ) - - case 2: return try .InvalidMessage( - message: FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: ReceiveSyncError, into buf: inout [UInt8]) { - switch value { - case let .Internal(message): - writeInt(&buf, Int32(1)) - FfiConverterString.write(message, into: &buf) - case let .InvalidMessage(message): - writeInt(&buf, Int32(2)) - FfiConverterString.write(message, into: &buf) - } - } -} - -extension ReceiveSyncError: Equatable, Hashable {} - -extension ReceiveSyncError: Error {} - private struct FfiConverterOptionTypeValue: FfiConverterRustBuffer { typealias SwiftType = Value? @@ -2066,7 +2266,7 @@ private struct FfiConverterSequenceUInt8: FfiConverterRustBuffer { var seq = [UInt8]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - try seq.append(FfiConverterUInt8.read(from: &buf)) + seq.append(try FfiConverterUInt8.read(from: &buf)) } return seq } @@ -2088,7 +2288,7 @@ private struct FfiConverterSequenceString: FfiConverterRustBuffer { var seq = [String]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - try seq.append(FfiConverterString.read(from: &buf)) + seq.append(try FfiConverterString.read(from: &buf)) } return seq } @@ -2110,7 +2310,29 @@ private struct FfiConverterSequenceTypeKeyValue: FfiConverterRustBuffer { var seq = [KeyValue]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - try seq.append(FfiConverterTypeKeyValue.read(from: &buf)) + seq.append(try FfiConverterTypeKeyValue.read(from: &buf)) + } + return seq + } +} + +private struct FfiConverterSequenceTypeMark: FfiConverterRustBuffer { + typealias SwiftType = [Mark] + + public static func write(_ value: [Mark], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeMark.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Mark] { + let len: Int32 = try readInt(&buf) + var seq = [Mark]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeMark.read(from: &buf)) } return seq } @@ -2132,7 +2354,7 @@ private struct FfiConverterSequenceTypePatch: FfiConverterRustBuffer { var seq = [Patch]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - try seq.append(FfiConverterTypePatch.read(from: &buf)) + seq.append(try FfiConverterTypePatch.read(from: &buf)) } return seq } @@ -2154,7 +2376,7 @@ private struct FfiConverterSequenceTypePathElement: FfiConverterRustBuffer { var seq = [PathElement]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - try seq.append(FfiConverterTypePathElement.read(from: &buf)) + seq.append(try FfiConverterTypePathElement.read(from: &buf)) } return seq } @@ -2176,7 +2398,7 @@ private struct FfiConverterSequenceTypeScalarValue: FfiConverterRustBuffer { var seq = [ScalarValue]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - try seq.append(FfiConverterTypeScalarValue.read(from: &buf)) + seq.append(try FfiConverterTypeScalarValue.read(from: &buf)) } return seq } @@ -2198,7 +2420,7 @@ private struct FfiConverterSequenceTypeValue: FfiConverterRustBuffer { var seq = [Value]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - try seq.append(FfiConverterTypeValue.read(from: &buf)) + seq.append(try FfiConverterTypeValue.read(from: &buf)) } return seq } @@ -2220,12 +2442,35 @@ private struct FfiConverterSequenceTypeChangeHash: FfiConverterRustBuffer { var seq = [ChangeHash]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - try seq.append(FfiConverterTypeChangeHash.read(from: &buf)) + seq.append(try FfiConverterTypeChangeHash.read(from: &buf)) } return seq } } +private struct FfiConverterDictionaryStringTypeValue: FfiConverterRustBuffer { + public static func write(_ value: [String: Value], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for (key, value) in value { + FfiConverterString.write(key, into: &buf) + FfiConverterTypeValue.write(value, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String: Value] { + let len: Int32 = try readInt(&buf) + var dict = [String: Value]() + dict.reserveCapacity(Int(len)) + for _ in 0 ..< len { + let key = try FfiConverterString.read(from: &buf) + let value = try FfiConverterTypeValue.read(from: &buf) + dict[key] = value + } + return dict + } +} + /** * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. @@ -2297,22 +2542,216 @@ public struct FfiConverterTypeObjId: FfiConverter { public func root() -> ObjId { try! FfiConverterTypeObjId.lift( - try! - - rustCall { - automerge_9b9_root($0) - } + try! rustCall { + uniffi_automerge_fn_func_root($0) + } ) } -/** - * Top level initializers and tear down methods. - * - * This is generated by uniffi. - */ -public enum AutomergeLifecycle { - /** - * Initialize the FFI and Rust library. This should be only called once per application. - */ - func initialize() {} +private enum InitializationResult { + case ok + case contractVersionMismatch + case apiChecksumMismatch +} + +// Use a global variables to perform the versioning checks. Swift ensures that +// the code inside is only computed once. +private var initializationResult: InitializationResult { + // Get the bindings contract version from our ComponentInterface + let bindings_contract_version = 22 + // Get the scaffolding contract version by calling the into the dylib + let scaffolding_contract_version = ffi_automerge_uniffi_contract_version() + if bindings_contract_version != scaffolding_contract_version { + return InitializationResult.contractVersionMismatch + } + if uniffi_automerge_checksum_func_root() != 26077 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_syncstate_encode() != 59150 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_syncstate_reset() != 5568 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_syncstate_their_heads() != 64411 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_actor_id() != 11490 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_set_actor() != 35416 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_fork() != 25657 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_fork_at() != 50476 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_put_in_map() != 35151 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_put_object_in_map() != 62590 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_put_in_list() != 4528 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_put_object_in_list() != 12042 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_insert_in_list() != 50457 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_insert_object_in_list() != 44319 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_splice_text() != 50590 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_splice() != 26727 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_mark() != 20101 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_marks() != 22398 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_marks_at() != 56398 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_delete_in_map() != 14795 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_delete_in_list() != 11486 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_increment_in_map() != 10806 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_increment_in_list() != 11835 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_get_in_map() != 29552 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_get_in_list() != 15714 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_get_at_in_map() != 4821 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_get_at_in_list() != 35364 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_get_all_in_map() != 65368 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_get_all_in_list() != 9030 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_get_all_at_in_map() != 60892 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_get_all_at_in_list() != 65173 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_text() != 64459 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_text_at() != 64255 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_map_keys() != 53447 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_map_keys_at() != 37789 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_map_entries() != 35449 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_map_entries_at() != 19141 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_values() != 34066 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_values_at() != 26130 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_length() != 24325 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_length_at() != 12090 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_object_type() != 62959 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_path() != 39894 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_heads() != 41278 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_save() != 57703 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_merge() != 42744 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_merge_with_patches() != 34457 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_generate_sync_message() != 17944 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_receive_sync_message() != 35482 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_receive_sync_message_with_patches() != 36417 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_encode_new_changes() != 27855 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_encode_changes_since() != 54354 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_apply_encoded_changes() != 54766 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_method_doc_apply_encoded_changes_with_patches() != 531 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_constructor_syncstate_new() != 17106 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_constructor_syncstate_decode() != 23331 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_constructor_doc_new() != 17962 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_constructor_doc_new_with_actor() != 23025 { + return InitializationResult.apiChecksumMismatch + } + if uniffi_automerge_checksum_constructor_doc_load() != 35192 { + return InitializationResult.apiChecksumMismatch + } + + return InitializationResult.ok +} + +private func uniffiEnsureInitialized() { + switch initializationResult { + case .ok: + break + case .contractVersionMismatch: + fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project") + case .apiChecksumMismatch: + fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } } diff --git a/AutomergeUniffi/automergeFFI.h b/AutomergeUniffi/automergeFFI.h index a07a140b..aa03ec72 100644 --- a/AutomergeUniffi/automergeFFI.h +++ b/AutomergeUniffi/automergeFFI.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include // The following structs are used to implement the lowest level @@ -28,7 +29,19 @@ typedef struct RustBuffer uint8_t *_Nullable data; } RustBuffer; -typedef int32_t (*ForeignCallback)(uint64_t, int32_t, RustBuffer, RustBuffer *_Nonnull); +typedef int32_t (*ForeignCallback)(uint64_t, int32_t, const uint8_t *_Nonnull, int32_t, RustBuffer *_Nonnull); + +// Task defined in Rust that Swift executes +typedef void (*UniFfiRustTaskCallback)(const void * _Nullable); + +// Callback to execute Rust tasks using a Swift Task +// +// Args: +// executor: ForeignExecutor lowered into a size_t value +// delay: Delay in MS +// task: UniFfiRustTaskCallback to call +// task_data: data to pass the task callback +typedef void (*UniFfiForeignExecutorCallback)(size_t, uint32_t, UniFfiRustTaskCallback _Nullable, const void * _Nullable); typedef struct ForeignBytes { @@ -46,251 +59,325 @@ typedef struct RustCallStatus { // ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️ #endif // def UNIFFI_SHARED_H -void ffi_automerge_9b9_SyncState_object_free( - void*_Nonnull ptr, - RustCallStatus *_Nonnull out_status - ); -void*_Nonnull automerge_9b9_SyncState_new( - - RustCallStatus *_Nonnull out_status - ); -void*_Nonnull automerge_9b9_SyncState_decode( - RustBuffer bytes, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_SyncState_encode( - void*_Nonnull ptr, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_SyncState_reset( - void*_Nonnull ptr, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_SyncState_their_heads( - void*_Nonnull ptr, - RustCallStatus *_Nonnull out_status - ); -void ffi_automerge_9b9_Doc_object_free( - void*_Nonnull ptr, - RustCallStatus *_Nonnull out_status - ); -void*_Nonnull automerge_9b9_Doc_new( - - RustCallStatus *_Nonnull out_status - ); -void*_Nonnull automerge_9b9_Doc_new_with_actor( - RustBuffer actor, - RustCallStatus *_Nonnull out_status - ); -void*_Nonnull automerge_9b9_Doc_load( - RustBuffer bytes, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_actor_id( - void*_Nonnull ptr, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_set_actor( - void*_Nonnull ptr,RustBuffer actor, - RustCallStatus *_Nonnull out_status - ); -void*_Nonnull automerge_9b9_Doc_fork( - void*_Nonnull ptr, - RustCallStatus *_Nonnull out_status - ); -void*_Nonnull automerge_9b9_Doc_fork_at( - void*_Nonnull ptr,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_put_in_map( - void*_Nonnull ptr,RustBuffer obj,RustBuffer key,RustBuffer value, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_put_object_in_map( - void*_Nonnull ptr,RustBuffer obj,RustBuffer key,RustBuffer obj_type, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_put_in_list( - void*_Nonnull ptr,RustBuffer obj,uint64_t index,RustBuffer value, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_put_object_in_list( - void*_Nonnull ptr,RustBuffer obj,uint64_t index,RustBuffer obj_type, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_insert_in_list( - void*_Nonnull ptr,RustBuffer obj,uint64_t index,RustBuffer value, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_insert_object_in_list( - void*_Nonnull ptr,RustBuffer obj,uint64_t index,RustBuffer obj_type, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_splice_text( - void*_Nonnull ptr,RustBuffer obj,uint64_t start,uint64_t delete,RustBuffer chars, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_splice( - void*_Nonnull ptr,RustBuffer obj,uint64_t start,uint64_t delete,RustBuffer values, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_delete_in_map( - void*_Nonnull ptr,RustBuffer obj,RustBuffer key, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_delete_in_list( - void*_Nonnull ptr,RustBuffer obj,uint64_t index, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_increment_in_map( - void*_Nonnull ptr,RustBuffer obj,RustBuffer key,int64_t by, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_increment_in_list( - void*_Nonnull ptr,RustBuffer obj,uint64_t index,int64_t by, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_get_in_map( - void*_Nonnull ptr,RustBuffer obj,RustBuffer key, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_get_in_list( - void*_Nonnull ptr,RustBuffer obj,uint64_t index, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_get_at_in_map( - void*_Nonnull ptr,RustBuffer obj,RustBuffer key,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_get_at_in_list( - void*_Nonnull ptr,RustBuffer obj,uint64_t index,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_get_all_in_map( - void*_Nonnull ptr,RustBuffer obj,RustBuffer key, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_get_all_in_list( - void*_Nonnull ptr,RustBuffer obj,uint64_t index, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_get_all_at_in_map( - void*_Nonnull ptr,RustBuffer obj,RustBuffer key,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_get_all_at_in_list( - void*_Nonnull ptr,RustBuffer obj,uint64_t index,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_text( - void*_Nonnull ptr,RustBuffer obj, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_text_at( - void*_Nonnull ptr,RustBuffer obj,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_map_keys( - void*_Nonnull ptr,RustBuffer obj, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_map_keys_at( - void*_Nonnull ptr,RustBuffer obj,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_map_entries( - void*_Nonnull ptr,RustBuffer obj, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_map_entries_at( - void*_Nonnull ptr,RustBuffer obj,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_values( - void*_Nonnull ptr,RustBuffer obj, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_values_at( - void*_Nonnull ptr,RustBuffer obj,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -uint64_t automerge_9b9_Doc_length( - void*_Nonnull ptr,RustBuffer obj, - RustCallStatus *_Nonnull out_status - ); -uint64_t automerge_9b9_Doc_length_at( - void*_Nonnull ptr,RustBuffer obj,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_object_type( - void*_Nonnull ptr,RustBuffer obj, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_path( - void*_Nonnull ptr,RustBuffer obj, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_heads( - void*_Nonnull ptr, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_save( - void*_Nonnull ptr, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_merge( - void*_Nonnull ptr,void*_Nonnull other, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_merge_with_patches( - void*_Nonnull ptr,void*_Nonnull other, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_generate_sync_message( - void*_Nonnull ptr,void*_Nonnull state, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_receive_sync_message( - void*_Nonnull ptr,void*_Nonnull state,RustBuffer msg, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_receive_sync_message_with_patches( - void*_Nonnull ptr,void*_Nonnull state,RustBuffer msg, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_encode_new_changes( - void*_Nonnull ptr, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_encode_changes_since( - void*_Nonnull ptr,RustBuffer heads, - RustCallStatus *_Nonnull out_status - ); -void automerge_9b9_Doc_apply_encoded_changes( - void*_Nonnull ptr,RustBuffer changes, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_Doc_apply_encoded_changes_with_patches( - void*_Nonnull ptr,RustBuffer changes, - RustCallStatus *_Nonnull out_status - ); -RustBuffer automerge_9b9_root( - - RustCallStatus *_Nonnull out_status - ); -RustBuffer ffi_automerge_9b9_rustbuffer_alloc( - int32_t size, - RustCallStatus *_Nonnull out_status - ); -RustBuffer ffi_automerge_9b9_rustbuffer_from_bytes( - ForeignBytes bytes, - RustCallStatus *_Nonnull out_status - ); -void ffi_automerge_9b9_rustbuffer_free( - RustBuffer buf, - RustCallStatus *_Nonnull out_status - ); -RustBuffer ffi_automerge_9b9_rustbuffer_reserve( - RustBuffer buf,int32_t additional, - RustCallStatus *_Nonnull out_status - ); +// Callbacks for UniFFI Futures +typedef void (*UniFfiFutureCallbackUInt8)(const void * _Nonnull, uint8_t, RustCallStatus); +typedef void (*UniFfiFutureCallbackUInt64)(const void * _Nonnull, uint64_t, RustCallStatus); +typedef void (*UniFfiFutureCallbackUnsafeMutableRawPointer)(const void * _Nonnull, void*_Nonnull, RustCallStatus); +typedef void (*UniFfiFutureCallbackUnsafeMutableRawPointer)(const void * _Nonnull, void*_Nonnull, RustCallStatus); +typedef void (*UniFfiFutureCallbackRustBuffer)(const void * _Nonnull, RustBuffer, RustCallStatus); + +// Scaffolding functions +void uniffi_automerge_fn_free_syncstate(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +void*_Nonnull uniffi_automerge_fn_constructor_syncstate_new(RustCallStatus *_Nonnull out_status + +); +void*_Nonnull uniffi_automerge_fn_constructor_syncstate_decode(RustBuffer bytes, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_syncstate_encode(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_syncstate_reset(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_syncstate_their_heads(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_free_doc(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +void*_Nonnull uniffi_automerge_fn_constructor_doc_new(RustCallStatus *_Nonnull out_status + +); +void*_Nonnull uniffi_automerge_fn_constructor_doc_new_with_actor(RustBuffer actor, RustCallStatus *_Nonnull out_status +); +void*_Nonnull uniffi_automerge_fn_constructor_doc_load(RustBuffer bytes, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_actor_id(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_set_actor(void*_Nonnull ptr, RustBuffer actor, RustCallStatus *_Nonnull out_status +); +void*_Nonnull uniffi_automerge_fn_method_doc_fork(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +void*_Nonnull uniffi_automerge_fn_method_doc_fork_at(void*_Nonnull ptr, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_put_in_map(void*_Nonnull ptr, RustBuffer obj, RustBuffer key, RustBuffer value, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_put_object_in_map(void*_Nonnull ptr, RustBuffer obj, RustBuffer key, RustBuffer obj_type, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_put_in_list(void*_Nonnull ptr, RustBuffer obj, uint64_t index, RustBuffer value, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_put_object_in_list(void*_Nonnull ptr, RustBuffer obj, uint64_t index, RustBuffer obj_type, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_insert_in_list(void*_Nonnull ptr, RustBuffer obj, uint64_t index, RustBuffer value, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_insert_object_in_list(void*_Nonnull ptr, RustBuffer obj, uint64_t index, RustBuffer obj_type, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_splice_text(void*_Nonnull ptr, RustBuffer obj, uint64_t start, int64_t delete, RustBuffer chars, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_splice(void*_Nonnull ptr, RustBuffer obj, uint64_t start, int64_t delete, RustBuffer values, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_mark(void*_Nonnull ptr, RustBuffer obj, uint64_t start, uint64_t end, RustBuffer expand, RustBuffer name, RustBuffer value, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_marks(void*_Nonnull ptr, RustBuffer obj, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_marks_at(void*_Nonnull ptr, RustBuffer obj, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_delete_in_map(void*_Nonnull ptr, RustBuffer obj, RustBuffer key, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_delete_in_list(void*_Nonnull ptr, RustBuffer obj, uint64_t index, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_increment_in_map(void*_Nonnull ptr, RustBuffer obj, RustBuffer key, int64_t by, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_increment_in_list(void*_Nonnull ptr, RustBuffer obj, uint64_t index, int64_t by, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_get_in_map(void*_Nonnull ptr, RustBuffer obj, RustBuffer key, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_get_in_list(void*_Nonnull ptr, RustBuffer obj, uint64_t index, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_get_at_in_map(void*_Nonnull ptr, RustBuffer obj, RustBuffer key, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_get_at_in_list(void*_Nonnull ptr, RustBuffer obj, uint64_t index, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_get_all_in_map(void*_Nonnull ptr, RustBuffer obj, RustBuffer key, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_get_all_in_list(void*_Nonnull ptr, RustBuffer obj, uint64_t index, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_get_all_at_in_map(void*_Nonnull ptr, RustBuffer obj, RustBuffer key, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_get_all_at_in_list(void*_Nonnull ptr, RustBuffer obj, uint64_t index, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_text(void*_Nonnull ptr, RustBuffer obj, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_text_at(void*_Nonnull ptr, RustBuffer obj, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_map_keys(void*_Nonnull ptr, RustBuffer obj, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_map_keys_at(void*_Nonnull ptr, RustBuffer obj, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_map_entries(void*_Nonnull ptr, RustBuffer obj, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_map_entries_at(void*_Nonnull ptr, RustBuffer obj, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_values(void*_Nonnull ptr, RustBuffer obj, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_values_at(void*_Nonnull ptr, RustBuffer obj, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +uint64_t uniffi_automerge_fn_method_doc_length(void*_Nonnull ptr, RustBuffer obj, RustCallStatus *_Nonnull out_status +); +uint64_t uniffi_automerge_fn_method_doc_length_at(void*_Nonnull ptr, RustBuffer obj, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_object_type(void*_Nonnull ptr, RustBuffer obj, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_path(void*_Nonnull ptr, RustBuffer obj, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_heads(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_save(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_merge(void*_Nonnull ptr, void*_Nonnull other, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_merge_with_patches(void*_Nonnull ptr, void*_Nonnull other, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_generate_sync_message(void*_Nonnull ptr, void*_Nonnull state, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_receive_sync_message(void*_Nonnull ptr, void*_Nonnull state, RustBuffer msg, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_receive_sync_message_with_patches(void*_Nonnull ptr, void*_Nonnull state, RustBuffer msg, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_encode_new_changes(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_encode_changes_since(void*_Nonnull ptr, RustBuffer heads, RustCallStatus *_Nonnull out_status +); +void uniffi_automerge_fn_method_doc_apply_encoded_changes(void*_Nonnull ptr, RustBuffer changes, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_method_doc_apply_encoded_changes_with_patches(void*_Nonnull ptr, RustBuffer changes, RustCallStatus *_Nonnull out_status +); +RustBuffer uniffi_automerge_fn_func_root(RustCallStatus *_Nonnull out_status + +); +RustBuffer ffi_automerge_rustbuffer_alloc(int32_t size, RustCallStatus *_Nonnull out_status +); +RustBuffer ffi_automerge_rustbuffer_from_bytes(ForeignBytes bytes, RustCallStatus *_Nonnull out_status +); +void ffi_automerge_rustbuffer_free(RustBuffer buf, RustCallStatus *_Nonnull out_status +); +RustBuffer ffi_automerge_rustbuffer_reserve(RustBuffer buf, int32_t additional, RustCallStatus *_Nonnull out_status +); +uint16_t uniffi_automerge_checksum_func_root(void + +); +uint16_t uniffi_automerge_checksum_method_syncstate_encode(void + +); +uint16_t uniffi_automerge_checksum_method_syncstate_reset(void + +); +uint16_t uniffi_automerge_checksum_method_syncstate_their_heads(void + +); +uint16_t uniffi_automerge_checksum_method_doc_actor_id(void + +); +uint16_t uniffi_automerge_checksum_method_doc_set_actor(void + +); +uint16_t uniffi_automerge_checksum_method_doc_fork(void + +); +uint16_t uniffi_automerge_checksum_method_doc_fork_at(void + +); +uint16_t uniffi_automerge_checksum_method_doc_put_in_map(void + +); +uint16_t uniffi_automerge_checksum_method_doc_put_object_in_map(void + +); +uint16_t uniffi_automerge_checksum_method_doc_put_in_list(void + +); +uint16_t uniffi_automerge_checksum_method_doc_put_object_in_list(void + +); +uint16_t uniffi_automerge_checksum_method_doc_insert_in_list(void + +); +uint16_t uniffi_automerge_checksum_method_doc_insert_object_in_list(void + +); +uint16_t uniffi_automerge_checksum_method_doc_splice_text(void + +); +uint16_t uniffi_automerge_checksum_method_doc_splice(void + +); +uint16_t uniffi_automerge_checksum_method_doc_mark(void + +); +uint16_t uniffi_automerge_checksum_method_doc_marks(void + +); +uint16_t uniffi_automerge_checksum_method_doc_marks_at(void + +); +uint16_t uniffi_automerge_checksum_method_doc_delete_in_map(void + +); +uint16_t uniffi_automerge_checksum_method_doc_delete_in_list(void + +); +uint16_t uniffi_automerge_checksum_method_doc_increment_in_map(void + +); +uint16_t uniffi_automerge_checksum_method_doc_increment_in_list(void + +); +uint16_t uniffi_automerge_checksum_method_doc_get_in_map(void + +); +uint16_t uniffi_automerge_checksum_method_doc_get_in_list(void + +); +uint16_t uniffi_automerge_checksum_method_doc_get_at_in_map(void + +); +uint16_t uniffi_automerge_checksum_method_doc_get_at_in_list(void + +); +uint16_t uniffi_automerge_checksum_method_doc_get_all_in_map(void + +); +uint16_t uniffi_automerge_checksum_method_doc_get_all_in_list(void + +); +uint16_t uniffi_automerge_checksum_method_doc_get_all_at_in_map(void + +); +uint16_t uniffi_automerge_checksum_method_doc_get_all_at_in_list(void + +); +uint16_t uniffi_automerge_checksum_method_doc_text(void + +); +uint16_t uniffi_automerge_checksum_method_doc_text_at(void + +); +uint16_t uniffi_automerge_checksum_method_doc_map_keys(void + +); +uint16_t uniffi_automerge_checksum_method_doc_map_keys_at(void + +); +uint16_t uniffi_automerge_checksum_method_doc_map_entries(void + +); +uint16_t uniffi_automerge_checksum_method_doc_map_entries_at(void + +); +uint16_t uniffi_automerge_checksum_method_doc_values(void + +); +uint16_t uniffi_automerge_checksum_method_doc_values_at(void + +); +uint16_t uniffi_automerge_checksum_method_doc_length(void + +); +uint16_t uniffi_automerge_checksum_method_doc_length_at(void + +); +uint16_t uniffi_automerge_checksum_method_doc_object_type(void + +); +uint16_t uniffi_automerge_checksum_method_doc_path(void + +); +uint16_t uniffi_automerge_checksum_method_doc_heads(void + +); +uint16_t uniffi_automerge_checksum_method_doc_save(void + +); +uint16_t uniffi_automerge_checksum_method_doc_merge(void + +); +uint16_t uniffi_automerge_checksum_method_doc_merge_with_patches(void + +); +uint16_t uniffi_automerge_checksum_method_doc_generate_sync_message(void + +); +uint16_t uniffi_automerge_checksum_method_doc_receive_sync_message(void + +); +uint16_t uniffi_automerge_checksum_method_doc_receive_sync_message_with_patches(void + +); +uint16_t uniffi_automerge_checksum_method_doc_encode_new_changes(void + +); +uint16_t uniffi_automerge_checksum_method_doc_encode_changes_since(void + +); +uint16_t uniffi_automerge_checksum_method_doc_apply_encoded_changes(void + +); +uint16_t uniffi_automerge_checksum_method_doc_apply_encoded_changes_with_patches(void + +); +uint16_t uniffi_automerge_checksum_constructor_syncstate_new(void + +); +uint16_t uniffi_automerge_checksum_constructor_syncstate_decode(void + +); +uint16_t uniffi_automerge_checksum_constructor_doc_new(void + +); +uint16_t uniffi_automerge_checksum_constructor_doc_new_with_actor(void + +); +uint16_t uniffi_automerge_checksum_constructor_doc_load(void + +); +uint32_t ffi_automerge_uniffi_contract_version(void + +); + diff --git a/Sources/Automerge/Automerge.docc/Automerge.md b/Sources/Automerge/Automerge.docc/Automerge.md index db9505bc..dcdb89b1 100644 --- a/Sources/Automerge/Automerge.docc/Automerge.md +++ b/Sources/Automerge/Automerge.docc/Automerge.md @@ -102,6 +102,8 @@ Any document method which accepts remote changes has a `*WithPatches` variant wh - ``Automerge/SchemaStrategy`` - ``Automerge/CodingKeyLookupError`` - ``Automerge/PathParseError`` +- ``Automerge/Mark`` +- ``Automerge/ExpandMark`` ### Converting Scalar Values to Local Types diff --git a/Sources/Automerge/Automerge.docc/Document.md b/Sources/Automerge/Automerge.docc/Document.md index c93b7a70..2bc14918 100644 --- a/Sources/Automerge/Automerge.docc/Document.md +++ b/Sources/Automerge/Automerge.docc/Document.md @@ -13,6 +13,7 @@ - ``ActorId`` - ``objectType(obj:)`` - ``path(obj:)`` +- ``lookupPath(path:)`` ### Reading maps @@ -48,10 +49,12 @@ - ``text(obj:)`` - ``length(obj:)`` +- ``marks(obj:)`` ### Updating Text values - ``spliceText(obj:start:delete:value:)`` +- ``mark(obj:start:end:expand:name:value:)`` ### Updating counters @@ -82,6 +85,7 @@ - ``textAt(obj:heads:)`` - ``lengthAt(obj:heads:)`` +- ``marksAt(obj:heads:)`` ### Saving, forking, and merging documents diff --git a/Sources/Automerge/Document.swift b/Sources/Automerge/Document.swift index 9f8335bd..2e6fb269 100644 --- a/Sources/Automerge/Document.swift +++ b/Sources/Automerge/Document.swift @@ -322,11 +322,12 @@ public class Document: @unchecked Sendable { /// Splice into the list `obj` /// /// - Parameters: - /// - obj: The list to insert into - /// - start: the index to begin inserting at - /// - delete: the number of elements to delete - /// - values: the values to insert - public func splice(obj: ObjId, start: UInt64, delete: UInt64, values: [ScalarValue]) throws { + /// - obj: The list to into which to insert. + /// - start: The index where the function begins inserting or deleting. + /// - delete: The number of elements to delete from the `start` index. + /// If negative, the function deletes elements preceding `start` index, rather than following it. + /// - values: The values to insert after the `start` index. + public func splice(obj: ObjId, start: UInt64, delete: Int64, values: [ScalarValue]) throws { try queue.sync { try self.doc.wrapErrors { try $0.splice( @@ -339,18 +340,22 @@ public class Document: @unchecked Sendable { /// Splice into the list `obj` /// /// - Parameters: - /// - obj: The list to insert into - /// - start: the index to begin inserting at IN UNICODE CODE POINTS - /// - delete: the number of elements to delete IN UNICODE CODE POINTS - /// - values: the characters to insert + /// - obj: The list into which to insert. + /// - start: The index position, in UTF-8 code points, where the function begins inserting or deleting. + /// - delete: The number of UTF-8 code points to delete from the `start` index. + /// If negative, the function deletes characters preceding `start` index, rather than following it. + /// - values: The characters to insert after the `start` index. /// - /// # Indexes - /// Swift string indexes represent grapheme clusters, but automerge works - /// in terms of UTF-8 code points. The indices to this method are utf-8 - /// code point indices. This means if you receive indices from other parts - /// of the application which are swift string indices you will need to - /// convert them. - public func spliceText(obj: ObjId, start: UInt64, delete: UInt64, value: String? = nil) throws { + /// With `spliceText`, the `start` and `delete` parameters represent UTF-8 + /// code point indexes. Swift string indexes represent grapheme clusters, but Automerge works + /// in terms of UTF-8 code points. This means if you receive indices from other parts + /// of the application which are swift string indices you need to convert them. + /// + /// It can be convenient to access the `UTF8View` of the String through it's `utf8` property, + /// or if you have a `String.Index` type, you can convert that into a + /// `String.UTF8View.Index` position using `samePosition` on the index with + /// a reference to the UTF-8 view of the string through its `utf8` property. + public func spliceText(obj: ObjId, start: UInt64, delete: Int64, value: String? = nil) throws { try queue.sync { try self.doc.wrapErrors { try $0.spliceText(obj: obj.bytes, start: start, delete: delete, chars: value ?? "") @@ -358,16 +363,54 @@ public class Document: @unchecked Sendable { } } - /// Encode this document in a compressed binary format + /// Add or remove a mark to a given range of text + /// + /// - Parameters: + /// - obj: The text object to which to add the mark. + /// - start: The index position, in UTF-8 code points, where the function starts the mark. + /// - end: The index position, in UTF-8 code points, where the function starts the mark. + /// - expand: How the mark should expand when text is inserted at the beginning or end of the range + /// - name: The name of the mark, for example "bold". + /// - value: The scalar value to associate with the mark. + /// + /// To remove an existing mark between two index positions, set the name to the same value + /// as the existing mark and set the value to the scalar value ``ScalarValue/Null``. + public func mark(obj: ObjId, start: UInt64, end: UInt64, expand: ExpandMark, name: String, value: ScalarValue) throws { + try queue.sync { + try self.doc.wrapErrors { + try $0.mark(obj: obj.bytes, start: start, end: end, expand: expand.toFfi(), name: name, value: value.toFfi()) + } + } + } + + /// Returns a list of marks for a text object. + public func marks(obj: ObjId) throws -> [Mark] { + try queue.sync { + try self.doc.wrapErrors { + try $0.marks(obj: obj.bytes).map(Mark.fromFfi) + } + } + } + + /// Get the list of marks for a text object at the given heads. + public func marksAt(obj: ObjId, heads: Set) throws -> [Mark] { + try queue.sync { + try self.doc.wrapErrors { + try $0.marksAt(obj: obj.bytes, heads: heads.map(\.bytes)).map(Mark.fromFfi) + } + } + } + + /// Encode this document in a compressed binary format. public func save() -> Data { queue.sync { self.doc.wrapErrors { Data($0.save()) } } } - /// Generate a sync message to send to the peer represented by `state` + /// Generate a sync message to send to the peer represented by `state`. /// - /// - Returns: A message to send to the peer, or `nil` if we are in sync + /// - Returns: A message to send to the peer, or `nil` if the Automerge documents are in sync. public func generateSyncMessage(state: SyncState) -> Data? { queue.sync { self.doc.wrapErrors { @@ -379,10 +422,10 @@ public class Document: @unchecked Sendable { } } - /// Receive a sync message from the peer represented by `state` + /// Receive a sync message from the peer represented by `state`. /// /// > Tip: if you need to know what changed in the document as a result of - /// the message try using ``receiveSyncMessageWithPatches(state:message:)`` + /// the message use the function ``receiveSyncMessageWithPatches(state:message:)``. public func receiveSyncMessage(state: SyncState, message: Data) throws { try queue.sync { try self.doc.wrapErrors { @@ -391,9 +434,12 @@ public class Document: @unchecked Sendable { } } - /// Receive a sync message from the peer represented by `state`, returning patches + /// Receive a sync message from the peer represented by `state`, returning patches. /// - /// Returns: a sequence of ``Patch`` representing the changes which were + /// - Parameters: + /// - state: The state of another Automerge document. + /// - message: The sync message to integrate into this document. + /// - Returns: a sequence of ``Patch`` representing the changes which were /// made to the document as a result of the message. public func receiveSyncMessageWithPatches(state: SyncState, message: Data) throws -> [Patch] { try queue.sync { diff --git a/Sources/Automerge/Marks.swift b/Sources/Automerge/Marks.swift new file mode 100644 index 00000000..120feda1 --- /dev/null +++ b/Sources/Automerge/Marks.swift @@ -0,0 +1,79 @@ +import struct AutomergeUniffi.Mark +import enum AutomergeUniffi.ExpandMark + +typealias FfiMark = AutomergeUniffi.Mark +typealias FfiExpandMark = AutomergeUniffi.ExpandMark + + +/// A type that represents a marked range of text. +/// +/// Marks are annotations to text, which can be used to identify additional formatting, or other indicators relevant to the text at a specific location. +/// The are identified by a string `name` and have an associated ``ScalarValue``. +public struct Mark: Equatable, Hashable, Sendable { + /// The utf-8 codepoint index of the start of the mark + public let start: UInt64 + /// The utf-8 codepoint index of the end of the mark + public let end: UInt64 + /// The name of the mark + public let name: String + /// The value associated with the mark + public let value: Value + + public init(start: UInt64, end: UInt64, name: String, value: Value) { + self.start = start + self.end = end + self.name = name + self.value = value + } + + static func fromFfi(_ ffiMark: FfiMark) -> Self { + Self(start: ffiMark.start, end: ffiMark.end, name: ffiMark.name, value: Value.fromFfi(value: ffiMark.value)) + } + +} + +/// A type that indicates how a mark should expand when adding characters at the ends of the mark. +/// +/// Typically there are two different kinds of mark: "bold" type marks, where +/// adding text at the ends of the mark is expected to expand the mark to +/// include the added text, and "link" type marks, where the marked text is _not_ +/// expected to expand when adding new characters. +/// +/// For more information on marks and how they expand, +/// see the [The Peritext Essay](https://www.inkandswitch.com/peritext/). +public enum ExpandMark { + /// Characters added just before the mark should be inside the mark. + case before + /// Characters added just after the mark should be inside the mark. + case after + /// Characters added just before or just after the mark should be inside the mark. + case both + /// Characters added just before or just after the mark should never be added to it. + case none + + static func fromFfi(_ ffiExp: FfiExpandMark) -> Self { + switch ffiExp { + case .before: + return ExpandMark.before + case .after: + return ExpandMark.after + case .both: + return ExpandMark.both + case .none: + return ExpandMark.none + } + } + + internal func toFfi() -> FfiExpandMark { + switch self { + case .before: + return FfiExpandMark.before + case .after: + return FfiExpandMark.after + case .both: + return FfiExpandMark.both + case .none: + return FfiExpandMark.none + } + } +} diff --git a/Sources/Automerge/Patch.swift b/Sources/Automerge/Patch.swift index b59f2eb5..3e027397 100644 --- a/Sources/Automerge/Patch.swift +++ b/Sources/Automerge/Patch.swift @@ -11,10 +11,10 @@ typealias FfiPatch = AutomergeUniffi.Patch /// - ``Document/receiveSyncMessageWithPatches(state:message:)`` /// - ``Document/mergeWithPatches(other:)``. /// -/// You can inspect these patches to identify the objects updated within the Automerge document, in order to react -/// accordingly within your code. -/// A common use case for inspecting patches is to recalculate derived data that is using Automerge as an authoritative -/// source. +/// You can inspect these patches to identify the objects updated within the Automerge document, +/// in order to react accordingly within your code. +/// A common use case for inspecting patches is to recalculate derived data that is using +/// Automerge as an authoritative source. public struct Patch: Equatable { /// The the type of change, and the value that patch updated, if relevant to the change. public let action: PatchAction @@ -25,7 +25,7 @@ public struct Patch: Equatable { /// the associated `action`. public let path: [PathElement] - /// Creates a new patch + /// Creates a new patch. /// - Parameters: /// - action: The kind of update to apply. /// - path: The path to the object identifier that the action effects. @@ -50,35 +50,57 @@ public enum PatchAction: Equatable { /// /// The property included within the `Put` can be either an index to a sequence, or a key into a map. case Put(ObjId, Prop, Value) - /// Insert a collection of values at the index you provide for the identified object. - case Insert(ObjId, UInt64, [Value]) + /// Insert a collection of values at the index you provide for the identified object with the given marks + /// + /// `marks` will only be set for text objects. + case Insert(obj: ObjId, index: UInt64, values: [Value], marks: [String: Value]) /// Splices characters into and/or removes characters from the identified object at a given position within it. /// /// > Note: The unsigned 64bit integer is the index to a UTF-8 code point, and not a grapheme cluster index. /// If you are working with `Characters` from a `String`, you will need to calculate the offset to insert it /// correctly. - case SpliceText(ObjId, UInt64, String) + /// + /// `marks` are the currently active marks for the inserted value + case SpliceText(obj: ObjId, index: UInt64, value: String, marks: [String: Value]) /// Increment the property of the identified object, typically a Counter. case Increment(ObjId, Prop, Int64) /// Delete a key from a identified object. case DeleteMap(ObjId, String) /// Delete a sequence from the identified object starting at the index you provide for the length you provide. case DeleteSeq(DeleteSeq) + /// Add marks to a text object. + case Marks(ObjId, [Mark]) + /// Flag that a property within an object is conflicted. + case Conflict(ObjId, Prop) static func fromFfi(_ ffi: FfiPatchAction) -> Self { switch ffi { case let .put(obj, prop, value): return .Put(ObjId(bytes: obj), Prop.fromFfi(prop), Value.fromFfi(value: value)) - case let .insert(obj, index, values): - return .Insert(ObjId(bytes: obj), index, values.map { Value.fromFfi(value: $0) }) - case let .spliceText(obj, index, value): - return .SpliceText(ObjId(bytes: obj), index, value) + case let .insert(obj, index, values, marks): + return .Insert( + obj: ObjId(bytes: obj), + index: index, + values: values.map { Value.fromFfi(value: $0) }, + marks: marks.mapValues(Value.fromFfi) + ) + case let .spliceText(obj, index, value, marks): + return .SpliceText( + obj: ObjId(bytes: obj), + index: index, + value: value, + marks: marks.mapValues(Value.fromFfi) + ) case let .increment(obj, prop, value): return .Increment(ObjId(bytes: obj), Prop.fromFfi(prop), value) case let .deleteMap(obj, key): return .DeleteMap(ObjId(bytes: obj), key) case let .deleteSeq(obj, index, length): return .DeleteSeq(Automerge.DeleteSeq(obj: ObjId(bytes: obj), index: index, length: length)) + case let .marks(obj, marks): + return .Marks(ObjId(bytes: obj), marks.map(Mark.fromFfi)) + case let .conflict(obj, prop): + return .Conflict(ObjId(bytes: obj), Prop.fromFfi(prop)) } } } diff --git a/Tests/AutomergeTests/TestMarks.swift b/Tests/AutomergeTests/TestMarks.swift new file mode 100644 index 00000000..b27aac5e --- /dev/null +++ b/Tests/AutomergeTests/TestMarks.swift @@ -0,0 +1,54 @@ +@testable import Automerge +import XCTest + +class MarksTestCase: XCTestCase { + func testCrudMarks() { + let doc = Document() + let text = try! doc.putObject(obj: ObjId.ROOT, key: "text", ty: ObjType.Text) + try! doc.spliceText(obj: text, start: 0, delete: 0, value: "Hello marks") + try! doc.mark(obj: text, start: 0, end: 5, expand: ExpandMark.none, name: "bold", value: ScalarValue.Boolean(true)) + let marks = try! doc.marks(obj:text) + let expectedMarks = [ + Mark(start: 0, end: 5, name: "bold", value: Value.Scalar(ScalarValue.Boolean(true))) + ] + XCTAssertEqual(marks, expectedMarks) + + // save the heads to use later in marksAt + let heads = doc.heads() + + // Now remove the mark + try! doc.mark(obj: text, start: 0, end: 5, expand: ExpandMark.none, name: "bold", value: ScalarValue.Null) + let marksAfterDelete = try! doc.marks(obj:text) + XCTAssertEqual(marksAfterDelete.count, 0) + + // Now check that marksAt still returns the marks + let marksAt = try! doc.marksAt(obj:text, heads: heads) + XCTAssertEqual(marksAt, expectedMarks) + } + + func testMarkPatches() { + let doc = Document() + let text = try! doc.putObject(obj: ObjId.ROOT, key: "text", ty: ObjType.Text) + try! doc.spliceText(obj: text, start: 0, delete: 0, value: "Hello marks") + + // Make the marks on a fork so we can see the marks in patches when we merge + let fork = doc.fork() + try! fork.mark(obj: text, start: 0, end: 5, expand: ExpandMark.none, name: "bold", value: ScalarValue.Boolean(true)) + let patches = try! doc.mergeWithPatches(other: fork) + let expectedMarks = [ + Mark(start: 0, end: 5, name: "bold", value: Value.Scalar(ScalarValue.Boolean(true))) + ] + XCTAssertEqual(patches, [Patch( + action: .Marks(text, expectedMarks), + path: [PathElement(obj: ObjId.ROOT,prop:.Key("text")) ] + )]) + + // Now splice some text in the fork and make sure the splice patch contains the marks + try! fork.spliceText(obj: text, start: 4, delete: 0, value: "oo") + let patchesAfterSplice = try! doc.mergeWithPatches(other: fork) + XCTAssertEqual(patchesAfterSplice, [Patch( + action: .SpliceText(obj: text, index: 4, value: "oo", marks: ["bold": .Scalar(.Boolean(true))]), + path: [PathElement(obj: ObjId.ROOT,prop:.Key("text")) ] + )]) + } +} diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 9ec8c1af..f7f73d8f 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -16,24 +16,28 @@ checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "askama" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139" +checksum = "47cbc3cf73fa8d9833727bbee4835ba5c421a0d65b72daf9a7b5d0e0f9cfb57e" dependencies = [ "askama_derive", "askama_escape", - "askama_shared", ] [[package]] name = "askama_derive" -version = "0.11.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71" +checksum = "c22fbe0413545c098358e56966ff22cdd039e10215ae213cfbd65032b119fc94" dependencies = [ - "askama_shared", + "basic-toml", + "mime", + "mime_guess", + "nom", "proc-macro2", - "syn", + "quote", + "serde", + "syn 2.0.22", ] [[package]] @@ -42,28 +46,11 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" -[[package]] -name = "askama_shared" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0" -dependencies = [ - "askama_escape", - "mime", - "mime_guess", - "nom", - "proc-macro2", - "quote", - "serde", - "syn", - "toml", -] - [[package]] name = "automerge" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001093dfcea0a077d36b6039d98e77a6b8cea9e625552ec206305cbbd3a69816" +checksum = "6c01e49bfe4b5de9f8687a6f7a4a76fbe16270a978e1c4e784eb35f6665e8a4f" dependencies = [ "flate2", "fxhash", @@ -79,6 +66,15 @@ dependencies = [ "uuid", ] +[[package]] +name = "basic-toml" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" +dependencies = [ + "serde", +] + [[package]] name = "bincode" version = "1.3.3" @@ -375,18 +371,18 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -414,7 +410,7 @@ checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -443,7 +439,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -500,6 +496,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "thiserror" version = "1.0.38" @@ -517,7 +524,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -564,7 +571,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -599,9 +606,9 @@ checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "uniffi" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71cc01459bc34cfe43fabf32b39f1228709bc6db1b3a664a92940af3d062376" +checksum = "6da26ba712a8547207ededc70f3e0952c09754be9516c320f71731d2f18daf3e" dependencies = [ "anyhow", "uniffi_build", @@ -620,14 +627,14 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbbba5103051c18f10b22f80a74439ddf7100273f217a547005d2735b2498994" +checksum = "29bff3ba24868022fc82e2f1558f3a0fdcc2655e1335459a35f25d1ec4ff1d0c" dependencies = [ "anyhow", "askama", - "bincode", "camino", + "cargo_metadata", "fs-err", "glob", "goblin", @@ -644,9 +651,9 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1a28368ff3d83717e3d3e2e15a66269c43488c3f036914131bb68892f29fb" +checksum = "52b7cd03e17b997469e5438d1a491c3b9e2d41c2a87c86fd91ba96e87aecba6a" dependencies = [ "anyhow", "camino", @@ -655,19 +662,19 @@ dependencies = [ [[package]] name = "uniffi_checksum_derive" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03de61393a42b4ad4984a3763c0600594ac3e57e5aaa1d05cede933958987c03" +checksum = "af98d58e238b6aef9ff62a93b5c60caa710bdb49351434a639b9bd7b4c84c808" dependencies = [ "quote", - "syn", + "syn 2.0.22", ] [[package]] name = "uniffi_core" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2b4852d638d74ca2d70e450475efb6d91fe6d54a7cd8d6bd80ad2ee6cd7daa" +checksum = "68640fa1b5dfbb4ccc149057c81b40adc51a01d295ce798c15c6c76f7e899907" dependencies = [ "anyhow", "bytes", @@ -681,9 +688,9 @@ dependencies = [ [[package]] name = "uniffi_macros" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa03394de21e759e0022f1ea8d992d2e39290d735b9ed52b1f74b20a684f794e" +checksum = "76f72684ff48a8ff0ee95fde6dbcfa687236ad1789dc18205cb3305432a7b35c" dependencies = [ "bincode", "camino", @@ -692,7 +699,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 2.0.22", "toml", "uniffi_build", "uniffi_meta", @@ -700,20 +707,23 @@ dependencies = [ [[package]] name = "uniffi_meta" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fdab2c436aed7a6391bec64204ec33948bfed9b11b303235740771f85c4ea6" +checksum = "fe3388a58b13dad8f0cdcbdee1c59af6408608ce8d85a3ef5d1429369ca7b217" dependencies = [ + "anyhow", + "bytes", "serde", "siphasher", "uniffi_checksum_derive", + "uniffi_core", ] [[package]] name = "uniffi_testing" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b0570953ec41d97ce23e3b92161ac18231670a1f97523258a6d2ab76d7f76c" +checksum = "4fb437a2c8565249274e381fd88bc75b539897f321b79022c9fe7e275d2c2bbb" dependencies = [ "anyhow", "camino", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 2a42228c..d8108f50 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -13,10 +13,10 @@ path = "uniffi-bindgen.rs" required-features = ["uniffi/cli"] [dependencies] -automerge = "0.3.0" +automerge = "0.5.0" thiserror = "1.0.38" -uniffi = "0.23.0" +uniffi = "0.24.1" [build-dependencies] -uniffi = {version = "0.23.0", features = ["build"] } +uniffi = {version = "0.24.1", features = ["build"] } diff --git a/rust/src/automerge.udl b/rust/src/automerge.udl index 0ca97cb6..e1dba3e6 100644 --- a/rust/src/automerge.udl +++ b/rust/src/automerge.udl @@ -81,6 +81,20 @@ interface Prop { Index( u64 value ); }; +enum ExpandMark { + "Before", + "After", + "None", + "Both", +}; + +dictionary Mark { + u64 start; + u64 end; + string name; + Value value; +}; + dictionary PathElement { Prop prop; ObjId obj; @@ -94,11 +108,13 @@ dictionary Patch { [Enum] interface PatchAction { Put( ObjId obj, Prop prop, Value value); - Insert( ObjId obj, u64 index, sequence values); - SpliceText( ObjId obj, u64 index, string value); + Insert( ObjId obj, u64 index, sequence values, record marks); + SpliceText( ObjId obj, u64 index, string value, record marks); Increment( ObjId obj, Prop prop, i64 value); + Conflict( ObjId obj, Prop prop); DeleteMap( ObjId obj, string key); DeleteSeq( ObjId obj, u64 index, u64 length); + Marks( ObjId obj, sequence marks ); }; interface Doc { @@ -129,10 +145,17 @@ interface Doc { ObjId insert_object_in_list(ObjId obj, u64 index, ObjType obj_type); [Throws=DocError] - void splice_text(ObjId obj, u64 start, u64 delete, string chars); + void splice_text(ObjId obj, u64 start, i64 delete, string chars); [Throws=DocError] - void splice(ObjId obj, u64 start, u64 delete, sequence values); + void splice(ObjId obj, u64 start, i64 delete, sequence values); + + [Throws=DocError] + void mark(ObjId obj, u64 start, u64 end, ExpandMark expand, string name, ScalarValue value); + [Throws=DocError] + sequence marks(ObjId obj); + [Throws=DocError] + sequence marks_at(ObjId obj, sequence heads); [Throws=DocError] void delete_in_map(ObjId obj, string key); diff --git a/rust/src/doc.rs b/rust/src/doc.rs index 1b2cc624..8fe7044a 100644 --- a/rust/src/doc.rs +++ b/rust/src/doc.rs @@ -1,12 +1,12 @@ use std::sync::{Arc, RwLock, RwLockWriteGuard}; use am::sync::SyncDoc; -use am::transaction::Observed; use automerge as am; use automerge::{transaction::Transactable, ReadDoc}; use crate::actor_id::ActorId; -use crate::patches::{Observer, Patch}; +use crate::mark::{ExpandMark, Mark}; +use crate::patches::Patch; use crate::{ChangeHash, ObjId, ObjType, PathElement, ScalarValue, SyncState, Value}; #[derive(Debug, thiserror::Error)] @@ -36,15 +36,21 @@ pub struct KeyValue { pub value: Value, } -pub struct Doc(RwLock>>); +pub struct Doc(RwLock); + +// These are okay because on the swift side we wrap all accesses of the +// document to ensure they are only accessed from a single thread +unsafe impl Send for Doc {} +unsafe impl Sync for Doc {} + impl Doc { pub(crate) fn new() -> Self { - Self(RwLock::new(automerge::AutoCommitWithObs::default())) + Self(RwLock::new(automerge::AutoCommit::default())) } pub(crate) fn new_with_actor(actor: ActorId) -> Self { Self(RwLock::new( - am::AutoCommitWithObs::default().with_actor(actor.into()), + am::AutoCommit::default().with_actor(actor.into()), )) } @@ -261,9 +267,9 @@ impl Doc { assert_map(&*doc, &obj)?; Ok(doc .map_range(&obj, ..) - .map(|(k, v, id)| KeyValue { - key: k.into(), - value: (v, id).into(), + .map(|am::iter::MapRangeItem { key, value, id, .. }| KeyValue { + key: key.into(), + value: (value, id).into(), }) .collect::>()) } @@ -279,9 +285,9 @@ impl Doc { assert_map(&*doc, &obj)?; Ok(doc .map_range_at(&obj, .., &heads) - .map(|(k, v, id)| KeyValue { - key: k.into(), - value: (v, id).into(), + .map(|am::iter::MapRangeItem { key, value, id, .. }| KeyValue { + key: key.into(), + value: (value, id).into(), }) .collect::>()) } @@ -349,13 +355,13 @@ impl Doc { &self, obj: ObjId, start: u64, - delete: u64, + delete: i64, value: String, ) -> Result<(), DocError> { let obj = am::ObjId::from(obj); let mut doc = self.0.write().unwrap(); assert_text(&*doc, &obj)?; - doc.splice_text(&obj, start as usize, delete as usize, value.as_str())?; + doc.splice_text(&obj, start as usize, delete as isize, value.as_str())?; Ok(()) } @@ -363,7 +369,7 @@ impl Doc { &self, obj: ObjId, start: u64, - delete: u64, + delete: i64, values: Vec, ) -> Result<(), DocError> { let obj = am::ObjId::from(obj); @@ -372,12 +378,55 @@ impl Doc { doc.splice( &obj, start as usize, - delete as usize, + delete as isize, values.into_iter().map(|i| i.into()), )?; Ok(()) } + pub fn mark( + &self, + obj: ObjId, + start: u64, + end: u64, + expand: ExpandMark, + name: String, + value: ScalarValue, + ) -> Result<(), DocError> { + let obj = am::ObjId::from(obj); + let mut doc = self.0.write().unwrap(); + assert_text(&*doc, &obj)?; + let mark = am::marks::Mark::new(name, value, start as usize, end as usize); + doc.mark(obj, mark, expand.into())?; + Ok(()) + } + + pub fn marks(&self, obj: ObjId) -> Result, DocError> { + let obj = am::ObjId::from(obj); + let doc = self.0.write().unwrap(); + assert_text(&*doc, &obj)?; + Ok(doc + .marks(obj)? + .into_iter() + .map(|m| Mark::from(&m)) + .collect()) + } + + pub fn marks_at(&self, obj: ObjId, heads: Vec) -> Result, DocError> { + let obj = am::ObjId::from(obj); + let doc = self.0.write().unwrap(); + assert_text(&*doc, &obj)?; + let heads = heads + .into_iter() + .map(am::ChangeHash::from) + .collect::>(); + Ok(doc + .marks_at(obj, &heads)? + .into_iter() + .map(|m| Mark::from(&m)) + .collect()) + } + pub fn merge(&self, other: Arc) -> Result<(), DocError> { let mut doc = self.0.write().unwrap(); let mut other = other.0.write().unwrap(); @@ -401,9 +450,7 @@ impl Doc { pub fn load(bytes: Vec) -> Result { let ac = automerge::AutoCommit::load(bytes.as_slice())?; - Ok(Doc(RwLock::new( - ac.with_observer(crate::patches::Observer::default()), - ))) + Ok(Doc(RwLock::new(ac))) } pub fn generate_sync_message(&self, sync_state: Arc) -> Option> { @@ -470,7 +517,7 @@ impl Doc { pub fn path(&self, obj: ObjId) -> Result, DocError> { let doc = self.0.read().unwrap(); let obj = am::ObjId::from(obj); - let path = doc.path_to_object(obj)?; + let path = doc.parents(obj)?.path(); Ok(path .into_iter() .map(|(id, prop)| PathElement::new(prop, id)) @@ -488,7 +535,7 @@ impl Doc { .into_iter() .map(am::ChangeHash::from) .collect::>(); - let changes = doc.get_changes(&heads)?; + let changes = doc.get_changes(&heads); let mut result = Vec::new(); for change in changes { result.extend(change.clone().bytes().as_ref()); @@ -498,6 +545,7 @@ impl Doc { pub fn apply_encoded_changes(&self, changes: Vec) -> Result<(), DocError> { let mut doc = self.0.write().unwrap(); + doc.reset_diff_cursor(); doc.load_incremental(&changes)?; Ok(()) } @@ -514,20 +562,18 @@ impl Doc { } fn do_with_patches( - mut doc: RwLockWriteGuard>>, + mut doc: RwLockWriteGuard, f: F, ) -> Result, E> where - F: FnOnce( - &mut RwLockWriteGuard>>, - ) -> Result<(), E>, + F: FnOnce(&mut RwLockWriteGuard) -> Result<(), E>, { - doc.observer().enable(true); // Note no early return so we get a chance to pop the patches + doc.update_diff_cursor(); let result = f(&mut doc); - let patches = doc.observer().take_patches(); - doc.observer().enable(false); + let am_patches = doc.diff_incremental(); result?; + let patches = am_patches.into_iter().map(|p| p.into()).collect(); Ok(patches) } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0be0ffda..e40a44ba 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -6,6 +6,8 @@ mod change_hash; use change_hash::ChangeHash; mod doc; use doc::{Doc, DocError, KeyValue, LoadError, ReceiveSyncError}; +mod mark; +use mark::{ExpandMark, Mark}; mod obj_id; use obj_id::{root, ObjId}; mod obj_type; diff --git a/rust/src/mark.rs b/rust/src/mark.rs new file mode 100644 index 00000000..b38d100a --- /dev/null +++ b/rust/src/mark.rs @@ -0,0 +1,41 @@ +use automerge as am; + +use crate::Value; + +pub enum ExpandMark { + Before, + After, + None, + Both, +} + +impl From for am::marks::ExpandMark { + fn from(value: ExpandMark) -> Self { + match value { + ExpandMark::Before => am::marks::ExpandMark::Before, + ExpandMark::After => am::marks::ExpandMark::After, + ExpandMark::None => am::marks::ExpandMark::None, + ExpandMark::Both => am::marks::ExpandMark::Both, + } + } +} + +pub struct Mark { + pub start: u64, + pub end: u64, + pub name: String, + pub value: Value, +} + +impl<'a> From<&'a am::marks::Mark<'a>> for Mark { + fn from(am_mark: &'a am::marks::Mark<'a>) -> Mark { + Mark { + start: am_mark.start as u64, + end: am_mark.end as u64, + name: am_mark.name().to_string(), + value: Value::Scalar { + value: am_mark.value().into(), + }, + } + } +} diff --git a/rust/src/patches.rs b/rust/src/patches.rs index 70ba1d3b..c06a3f33 100644 --- a/rust/src/patches.rs +++ b/rust/src/patches.rs @@ -1,22 +1,29 @@ -use automerge as am; +use std::collections::HashMap; -mod sequence_tree; -use sequence_tree::SequenceTree; +use automerge as am; use crate::{ + mark::Mark, obj_id::ObjId, path::{PathElement, Prop}, value::Value, }; -mod observer; -pub(crate) use observer::Observer; -use observer::Patch as ObserverPatch; pub struct Patch { pub path: Vec, pub action: PatchAction, } +impl From for Patch { + fn from(p: am::Patch) -> Self { + let action = PatchAction::from_am(p.obj, p.action); + Patch { + path: convert_path(p.path), + action, + } + } +} + pub enum PatchAction { Put { obj: ObjId, @@ -27,17 +34,23 @@ pub enum PatchAction { obj: ObjId, index: u64, values: Vec, + marks: HashMap, }, SpliceText { obj: ObjId, index: u64, value: String, + marks: HashMap, }, Increment { obj: ObjId, prop: Prop, value: i64, }, + Conflict { + obj: ObjId, + prop: Prop, + }, DeleteMap { obj: ObjId, key: String, @@ -47,103 +60,89 @@ pub enum PatchAction { index: u64, length: u64, }, + Marks { + obj: ObjId, + marks: Vec, + }, } -impl From for Patch { - fn from(value: ObserverPatch) -> Self { - match value { - ObserverPatch::PutMap { - obj, - path, - key, - value, - } => Patch { - path: convert_path(path), - action: PatchAction::Put { - obj: obj.into(), - prop: Prop::Key { value: key }, - value: value.into(), - }, +impl PatchAction { + fn from_am(obj: am::ObjId, am_action: am::PatchAction) -> PatchAction { + match am_action { + am::PatchAction::PutMap { key, value, .. } => PatchAction::Put { + obj: obj.into(), + prop: Prop::Key { value: key }, + value: value.into(), }, - ObserverPatch::PutSeq { - obj, - path, - index, - value, - } => Patch { - path: convert_path(path), - action: PatchAction::Put { - obj: obj.into(), - prop: Prop::Index { - value: index as u64, - }, - value: value.into(), + am::PatchAction::PutSeq { index, value, .. } => PatchAction::Put { + obj: obj.into(), + prop: Prop::Index { + value: index as u64, }, + value: value.into(), }, - ObserverPatch::Insert { - obj, - path, + am::PatchAction::Insert { index, values, - } => Patch { - path: convert_path(path), - action: PatchAction::Insert { - obj: obj.into(), - index: index as u64, - values: values - .into_iter() - .map(|(v, id)| Value::from((v.clone(), id.clone()))) - .collect(), - }, + marks, + } => PatchAction::Insert { + obj: obj.into(), + index: index as u64, + values: values + .into_iter() + .map(|(v, id, _conflict)| Value::from((v.clone(), id.clone()))) + .collect(), + marks: convert_marks(marks), }, - ObserverPatch::SpliceText { - obj, - path, + am::PatchAction::SpliceText { index, value, - } => Patch { - path: convert_path(path), - action: PatchAction::SpliceText { - obj: obj.into(), - index: index as u64, - value: value.into_iter().collect(), - }, + marks, + } => PatchAction::SpliceText { + obj: obj.into(), + index: index as u64, + value: value.make_string(), + marks: convert_marks(marks), }, - ObserverPatch::Increment { - obj, - path, - prop, + am::PatchAction::Increment { prop, value } => PatchAction::Increment { + obj: obj.into(), + prop: prop.into(), value, - } => Patch { - path: convert_path(path), - action: PatchAction::Increment { - obj: obj.into(), - prop: prop.into(), - value, - }, }, - ObserverPatch::DeleteMap { obj, path, key } => Patch { - path: convert_path(path), - action: PatchAction::DeleteMap { - obj: obj.into(), - key, - }, + am::PatchAction::Conflict { prop } => PatchAction::Conflict { + prop: prop.into(), + obj: obj.into(), }, - ObserverPatch::DeleteSeq { - obj, - path, - index, - length, - } => Patch { - path: convert_path(path), - action: PatchAction::DeleteSeq { - obj: obj.into(), - index: index as u64, - length: length as u64, - }, + am::PatchAction::DeleteMap { key } => PatchAction::DeleteMap { + obj: obj.into(), + key, }, + am::PatchAction::DeleteSeq { index, length } => PatchAction::DeleteSeq { + obj: obj.into(), + index: index as u64, + length: length as u64, + }, + am::PatchAction::Mark { marks } => PatchAction::Marks { + obj: obj.into(), + marks: marks.into_iter().map(|m| Mark::from(&m)).collect(), + }, + } + } +} + +fn convert_marks(am_marks: Option) -> HashMap { + let mut result = HashMap::new(); + if let Some(am_marks) = am_marks { + for (name, value) in am_marks.iter() { + result.insert( + name.to_string(), + Value::Scalar { + value: value.into(), + }, + ); } } + result } fn convert_path(p: Vec<(am::ObjId, am::Prop)>) -> Vec { diff --git a/rust/src/patches/observer.rs b/rust/src/patches/observer.rs deleted file mode 100644 index c1dd0749..00000000 --- a/rust/src/patches/observer.rs +++ /dev/null @@ -1,330 +0,0 @@ -use super::SequenceTree; -use automerge as am; -use automerge::ReadDoc; - -#[derive(Debug, Clone, Default)] -pub(crate) struct Observer { - enabled: bool, - patches: Vec, -} - -impl Observer { - pub(crate) fn take_patches(&mut self) -> Vec { - std::mem::take(&mut self.patches) - .into_iter() - .map(super::Patch::from) - .collect() - } - - pub(crate) fn enable(&mut self, enable: bool) -> bool { - if self.enabled && !enable { - self.patches.truncate(0) - } - let old_enabled = self.enabled; - self.enabled = enable; - old_enabled - } - - fn get_path( - &mut self, - doc: &R, - obj: &am::ObjId, - ) -> Option> { - match doc.parents(obj) { - Ok(parents) => parents.visible_path(), - Err(e) => { - automerge::log!("error generating patch : {:?}", e); - None - } - } - } -} - -#[derive(Debug, Clone)] -pub(crate) enum Patch { - PutMap { - obj: am::ObjId, - path: Vec<(am::ObjId, am::Prop)>, - key: String, - value: (am::Value<'static>, am::ObjId), - }, - PutSeq { - obj: am::ObjId, - path: Vec<(am::ObjId, am::Prop)>, - index: usize, - value: (am::Value<'static>, am::ObjId), - }, - Insert { - obj: am::ObjId, - path: Vec<(am::ObjId, am::Prop)>, - index: usize, - values: SequenceTree<(am::Value<'static>, am::ObjId)>, - }, - SpliceText { - obj: am::ObjId, - path: Vec<(am::ObjId, am::Prop)>, - index: usize, - value: SequenceTree, - }, - Increment { - obj: am::ObjId, - path: Vec<(am::ObjId, am::Prop)>, - prop: am::Prop, - value: i64, - }, - DeleteMap { - obj: am::ObjId, - path: Vec<(am::ObjId, am::Prop)>, - key: String, - }, - DeleteSeq { - obj: am::ObjId, - path: Vec<(am::ObjId, am::Prop)>, - index: usize, - length: usize, - }, -} - -impl am::OpObserver for Observer { - fn insert( - &mut self, - doc: &R, - obj: am::ObjId, - index: usize, - tagged_value: (am::Value<'_>, am::ObjId), - ) { - if self.enabled { - let value = (tagged_value.0.to_owned(), tagged_value.1); - if let Some(Patch::Insert { - obj: tail_obj, - index: tail_index, - values, - .. - }) = self.patches.last_mut() - { - let range = *tail_index..=*tail_index + values.len(); - if tail_obj == &obj && range.contains(&index) { - values.insert(index - *tail_index, value); - return; - } - } - if let Some(path) = self.get_path(doc, &obj) { - let mut values = SequenceTree::new(); - values.push(value); - let patch = Patch::Insert { - path, - obj, - index, - values, - }; - self.patches.push(patch); - } - } - } - - fn splice_text(&mut self, doc: &R, obj: am::ObjId, index: usize, value: &str) { - if self.enabled { - if let Some(Patch::SpliceText { - obj: tail_obj, - index: tail_index, - value: prev_value, - .. - }) = self.patches.last_mut() - { - let range = *tail_index..=*tail_index + prev_value.len(); - if tail_obj == &obj && range.contains(&index) { - let i = index - *tail_index; - for (n, ch) in value.chars().enumerate() { - prev_value.insert(i + n, ch) - } - return; - } - } - if let Some(path) = self.get_path(doc, &obj) { - let mut v = SequenceTree::new(); - for ch in value.chars() { - v.push(ch) - } - let patch = Patch::SpliceText { - path, - obj, - index, - value: v, - }; - self.patches.push(patch); - } - } - } - - fn delete_seq(&mut self, doc: &R, obj: am::ObjId, index: usize, length: usize) { - if self.enabled { - match self.patches.last_mut() { - Some(Patch::SpliceText { - obj: tail_obj, - index: tail_index, - value, - .. - }) => { - let range = *tail_index..*tail_index + value.len(); - if tail_obj == &obj - && range.contains(&index) - && range.contains(&(index + length - 1)) - { - for _ in 0..length { - value.remove(index - *tail_index); - } - return; - } - } - Some(Patch::Insert { - obj: tail_obj, - index: tail_index, - values, - .. - }) => { - let range = *tail_index..*tail_index + values.len(); - if tail_obj == &obj - && range.contains(&index) - && range.contains(&(index + length - 1)) - { - for _ in 0..length { - values.remove(index - *tail_index); - } - return; - } - } - Some(Patch::DeleteSeq { - obj: tail_obj, - index: tail_index, - length: tail_length, - .. - }) => { - if tail_obj == &obj && index == *tail_index { - *tail_length += length; - return; - } - } - _ => {} - } - if let Some(path) = self.get_path(doc, &obj) { - let patch = Patch::DeleteSeq { - path, - obj, - index, - length, - }; - self.patches.push(patch) - } - } - } - - fn delete_map(&mut self, doc: &R, obj: am::ObjId, key: &str) { - if self.enabled { - if let Some(path) = self.get_path(doc, &obj) { - let patch = Patch::DeleteMap { - path, - obj, - key: key.to_owned(), - }; - self.patches.push(patch) - } - } - } - - fn put( - &mut self, - doc: &R, - obj: am::ObjId, - prop: am::Prop, - tagged_value: (am::Value<'_>, am::ObjId), - _conflict: bool, - ) { - if self.enabled { - if let Some(path) = self.get_path(doc, &obj) { - let value = (tagged_value.0.to_owned(), tagged_value.1); - let patch = match prop { - am::Prop::Map(key) => Patch::PutMap { - path, - obj, - key, - value, - }, - am::Prop::Seq(index) => Patch::PutSeq { - path, - obj, - index, - value, - }, - }; - self.patches.push(patch); - } - } - } - - fn expose( - &mut self, - doc: &R, - obj: am::ObjId, - prop: am::Prop, - tagged_value: (am::Value<'_>, am::ObjId), - _conflict: bool, - ) { - if self.enabled { - if let Some(path) = self.get_path(doc, &obj) { - let value = (tagged_value.0.to_owned(), tagged_value.1); - let patch = match prop { - am::Prop::Map(key) => Patch::PutMap { - path, - obj, - key, - value, - }, - am::Prop::Seq(index) => Patch::PutSeq { - path, - obj, - index, - value, - }, - }; - self.patches.push(patch); - } - } - } - - fn increment( - &mut self, - doc: &R, - obj: am::ObjId, - prop: am::Prop, - tagged_value: (i64, am::ObjId), - ) { - if self.enabled { - if let Some(path) = self.get_path(doc, &obj) { - let value = tagged_value.0; - self.patches.push(Patch::Increment { - path, - obj, - prop, - value, - }) - } - } - } - - fn text_as_seq(&self) -> bool { - false - } -} - -impl automerge::op_observer::BranchableObserver for Observer { - fn merge(&mut self, other: &Self) { - self.patches.extend_from_slice(other.patches.as_slice()) - } - - fn branch(&self) -> Self { - Observer { - patches: vec![], - enabled: self.enabled, - } - } -} diff --git a/rust/src/patches/sequence_tree.rs b/rust/src/patches/sequence_tree.rs deleted file mode 100644 index 8034b46e..00000000 --- a/rust/src/patches/sequence_tree.rs +++ /dev/null @@ -1,492 +0,0 @@ -use std::{ - cmp::{min, Ordering}, - fmt::Debug, - mem, -}; - -pub(crate) const B: usize = 16; -pub(crate) type SequenceTree = SequenceTreeInternal; - -#[derive(Clone, Debug)] -pub(crate) struct SequenceTreeInternal { - root_node: Option>, -} - -#[derive(Clone, Debug, PartialEq)] -struct SequenceTreeNode { - elements: Vec, - children: Vec>, - length: usize, -} - -impl SequenceTreeInternal -where - T: Clone + Debug, -{ - /// Construct a new, empty, sequence. - pub(crate) fn new() -> Self { - Self { root_node: None } - } - - /// Get the length of the sequence. - pub(crate) fn len(&self) -> usize { - self.root_node.as_ref().map_or(0, |n| n.len()) - } - - /// Create an iterator through the sequence. - pub(crate) fn iter(&self) -> Iter<'_, T> { - Iter { - inner: self, - index: 0, - } - } - - /// Insert the `element` into the sequence at `index`. - /// - /// # Panics - /// - /// Panics if `index > len`. - pub(crate) fn insert(&mut self, index: usize, element: T) { - let old_len = self.len(); - if let Some(root) = self.root_node.as_mut() { - #[cfg(debug_assertions)] - root.check(); - - if root.is_full() { - let original_len = root.len(); - let new_root = SequenceTreeNode::new(); - - // move new_root to root position - let old_root = mem::replace(root, new_root); - - root.length += old_root.len(); - root.children.push(old_root); - root.split_child(0); - - assert_eq!(original_len, root.len()); - - // after splitting the root has one element and two children, find which child the - // index is in - let first_child_len = root.children[0].len(); - let (child, insertion_index) = if first_child_len < index { - (&mut root.children[1], index - (first_child_len + 1)) - } else { - (&mut root.children[0], index) - }; - root.length += 1; - child.insert_into_non_full_node(insertion_index, element) - } else { - root.insert_into_non_full_node(index, element) - } - } else { - self.root_node = Some(SequenceTreeNode { - elements: vec![element], - children: Vec::new(), - length: 1, - }) - } - assert_eq!(self.len(), old_len + 1, "{:#?}", self); - } - - /// Push the `element` onto the back of the sequence. - pub(crate) fn push(&mut self, element: T) { - let l = self.len(); - self.insert(l, element) - } - - /// Get the `element` at `index` in the sequence. - pub(crate) fn get(&self, index: usize) -> Option<&T> { - self.root_node.as_ref().and_then(|n| n.get(index)) - } - - /// Removes the element at `index` from the sequence. - /// - /// # Panics - /// - /// Panics if `index` is out of bounds. - pub(crate) fn remove(&mut self, index: usize) -> T { - if let Some(root) = self.root_node.as_mut() { - #[cfg(debug_assertions)] - let len = root.check(); - let old = root.remove(index); - - if root.elements.is_empty() { - if root.is_leaf() { - self.root_node = None; - } else { - self.root_node = Some(root.children.remove(0)); - } - } - - #[cfg(debug_assertions)] - debug_assert_eq!(len, self.root_node.as_ref().map_or(0, |r| r.check()) + 1); - old - } else { - panic!("remove from empty tree") - } - } -} - -impl SequenceTreeNode -where - T: Clone + Debug, -{ - fn new() -> Self { - Self { - elements: Vec::new(), - children: Vec::new(), - length: 0, - } - } - - pub(crate) fn len(&self) -> usize { - self.length - } - - fn is_leaf(&self) -> bool { - self.children.is_empty() - } - - fn is_full(&self) -> bool { - self.elements.len() >= 2 * B - 1 - } - - /// Returns the child index and the given index adjusted for the cumulative index before that - /// child. - fn find_child_index(&self, index: usize) -> (usize, usize) { - let mut cumulative_len = 0; - for (child_index, child) in self.children.iter().enumerate() { - if cumulative_len + child.len() >= index { - return (child_index, index - cumulative_len); - } else { - cumulative_len += child.len() + 1; - } - } - panic!("index not found in node") - } - - fn insert_into_non_full_node(&mut self, index: usize, element: T) { - assert!(!self.is_full()); - if self.is_leaf() { - self.length += 1; - self.elements.insert(index, element); - } else { - let (child_index, sub_index) = self.find_child_index(index); - let child = &mut self.children[child_index]; - - if child.is_full() { - self.split_child(child_index); - - // child structure has changed so we need to find the index again - let (child_index, sub_index) = self.find_child_index(index); - let child = &mut self.children[child_index]; - child.insert_into_non_full_node(sub_index, element); - } else { - child.insert_into_non_full_node(sub_index, element); - } - self.length += 1; - } - } - - // A utility function to split the child `full_child_index` of this node - // Note that `full_child_index` must be full when this function is called. - fn split_child(&mut self, full_child_index: usize) { - let original_len_self = self.len(); - - // Create a new node which is going to store (B-1) keys - // of the full child. - let mut successor_sibling = SequenceTreeNode::new(); - - let full_child = &mut self.children[full_child_index]; - let original_len = full_child.len(); - assert!(full_child.is_full()); - - successor_sibling.elements = full_child.elements.split_off(B); - - if !full_child.is_leaf() { - successor_sibling.children = full_child.children.split_off(B); - } - - let middle = full_child.elements.pop().unwrap(); - - full_child.length = - full_child.elements.len() + full_child.children.iter().map(|c| c.len()).sum::(); - - successor_sibling.length = successor_sibling.elements.len() - + successor_sibling - .children - .iter() - .map(|c| c.len()) - .sum::(); - - let z_len = successor_sibling.len(); - - let full_child_len = full_child.len(); - - self.children - .insert(full_child_index + 1, successor_sibling); - - self.elements.insert(full_child_index, middle); - - assert_eq!(full_child_len + z_len + 1, original_len, "{:#?}", self); - - assert_eq!(original_len_self, self.len()); - } - - fn remove_from_leaf(&mut self, index: usize) -> T { - self.length -= 1; - self.elements.remove(index) - } - - fn remove_element_from_non_leaf(&mut self, index: usize, element_index: usize) -> T { - self.length -= 1; - if self.children[element_index].elements.len() >= B { - let total_index = self.cumulative_index(element_index); - // recursively delete index - 1 in predecessor_node - let predecessor = self.children[element_index].remove(index - 1 - total_index); - // replace element with that one - mem::replace(&mut self.elements[element_index], predecessor) - } else if self.children[element_index + 1].elements.len() >= B { - // recursively delete index + 1 in successor_node - let total_index = self.cumulative_index(element_index + 1); - let successor = self.children[element_index + 1].remove(index + 1 - total_index); - // replace element with that one - mem::replace(&mut self.elements[element_index], successor) - } else { - let middle_element = self.elements.remove(element_index); - let successor_child = self.children.remove(element_index + 1); - self.children[element_index].merge(middle_element, successor_child); - - let total_index = self.cumulative_index(element_index); - self.children[element_index].remove(index - total_index) - } - } - - fn cumulative_index(&self, child_index: usize) -> usize { - self.children[0..child_index] - .iter() - .map(|c| c.len() + 1) - .sum() - } - - fn remove_from_internal_child(&mut self, index: usize, mut child_index: usize) -> T { - if self.children[child_index].elements.len() < B - && if child_index > 0 { - self.children[child_index - 1].elements.len() < B - } else { - true - } - && if child_index + 1 < self.children.len() { - self.children[child_index + 1].elements.len() < B - } else { - true - } - { - // if the child and its immediate siblings have B-1 elements merge the child - // with one sibling, moving an element from this node into the new merged node - // to be the median - - if child_index > 0 { - let middle = self.elements.remove(child_index - 1); - - // use the predessor sibling - let successor = self.children.remove(child_index); - child_index -= 1; - - self.children[child_index].merge(middle, successor); - } else { - let middle = self.elements.remove(child_index); - - // use the sucessor sibling - let successor = self.children.remove(child_index + 1); - - self.children[child_index].merge(middle, successor); - } - } else if self.children[child_index].elements.len() < B { - if child_index > 0 - && self - .children - .get(child_index - 1) - .map_or(false, |c| c.elements.len() >= B) - { - let last_element = self.children[child_index - 1].elements.pop().unwrap(); - assert!(!self.children[child_index - 1].elements.is_empty()); - self.children[child_index - 1].length -= 1; - - let parent_element = - mem::replace(&mut self.elements[child_index - 1], last_element); - - self.children[child_index] - .elements - .insert(0, parent_element); - self.children[child_index].length += 1; - - if let Some(last_child) = self.children[child_index - 1].children.pop() { - self.children[child_index - 1].length -= last_child.len(); - self.children[child_index].length += last_child.len(); - self.children[child_index].children.insert(0, last_child); - } - } else if self - .children - .get(child_index + 1) - .map_or(false, |c| c.elements.len() >= B) - { - let first_element = self.children[child_index + 1].elements.remove(0); - self.children[child_index + 1].length -= 1; - - assert!(!self.children[child_index + 1].elements.is_empty()); - - let parent_element = mem::replace(&mut self.elements[child_index], first_element); - - self.children[child_index].length += 1; - self.children[child_index].elements.push(parent_element); - - if !self.children[child_index + 1].is_leaf() { - let first_child = self.children[child_index + 1].children.remove(0); - self.children[child_index + 1].length -= first_child.len(); - self.children[child_index].length += first_child.len(); - - self.children[child_index].children.push(first_child); - } - } - } - self.length -= 1; - let total_index = self.cumulative_index(child_index); - self.children[child_index].remove(index - total_index) - } - - fn check(&self) -> usize { - let l = self.elements.len() + self.children.iter().map(|c| c.check()).sum::(); - assert_eq!(self.len(), l, "{:#?}", self); - - l - } - - pub(crate) fn remove(&mut self, index: usize) -> T { - let original_len = self.len(); - if self.is_leaf() { - let v = self.remove_from_leaf(index); - assert_eq!(original_len, self.len() + 1); - debug_assert_eq!(self.check(), self.len()); - v - } else { - let mut total_index = 0; - for (child_index, child) in self.children.iter().enumerate() { - match (total_index + child.len()).cmp(&index) { - Ordering::Less => { - // should be later on in the loop - total_index += child.len() + 1; - continue; - } - Ordering::Equal => { - let v = self.remove_element_from_non_leaf( - index, - min(child_index, self.elements.len() - 1), - ); - assert_eq!(original_len, self.len() + 1); - debug_assert_eq!(self.check(), self.len()); - return v; - } - Ordering::Greater => { - let v = self.remove_from_internal_child(index, child_index); - assert_eq!(original_len, self.len() + 1); - debug_assert_eq!(self.check(), self.len()); - return v; - } - } - } - panic!( - "index not found to remove {} {} {} {}", - index, - total_index, - self.len(), - self.check() - ); - } - } - - fn merge(&mut self, middle: T, successor_sibling: SequenceTreeNode) { - self.elements.push(middle); - self.elements.extend(successor_sibling.elements); - self.children.extend(successor_sibling.children); - self.length += successor_sibling.length + 1; - assert!(self.is_full()); - } - - pub(crate) fn get(&self, index: usize) -> Option<&T> { - if self.is_leaf() { - return self.elements.get(index); - } else { - let mut cumulative_len = 0; - for (child_index, child) in self.children.iter().enumerate() { - match (cumulative_len + child.len()).cmp(&index) { - Ordering::Less => { - cumulative_len += child.len() + 1; - } - Ordering::Equal => return self.elements.get(child_index), - Ordering::Greater => { - return child.get(index - cumulative_len); - } - } - } - } - None - } -} - -impl Default for SequenceTreeInternal -where - T: Clone + Debug, -{ - fn default() -> Self { - Self::new() - } -} - -impl PartialEq for SequenceTreeInternal -where - T: Clone + Debug + PartialEq, -{ - fn eq(&self, other: &Self) -> bool { - self.len() == other.len() && self.iter().zip(other.iter()).all(|(a, b)| a == b) - } -} - -impl<'a, T> IntoIterator for &'a SequenceTreeInternal -where - T: Clone + Debug, -{ - type Item = &'a T; - - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Self::IntoIter { - Iter { - inner: self, - index: 0, - } - } -} - -#[derive(Debug)] -pub struct Iter<'a, T> { - inner: &'a SequenceTreeInternal, - index: usize, -} - -impl<'a, T> Iterator for Iter<'a, T> -where - T: Clone + Debug, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - self.index += 1; - self.inner.get(self.index - 1) - } - - fn nth(&mut self, n: usize) -> Option { - self.index += n + 1; - self.inner.get(self.index - 1) - } -}