diff --git a/bindings/ios/ZxDocumentRuntime.xcframework/ios-arm64-simulator/libzx_document_ffi.a b/bindings/ios/ZxDocumentRuntime.xcframework/ios-arm64-simulator/libzx_document_ffi.a index f9e0d69..1cae318 100644 Binary files a/bindings/ios/ZxDocumentRuntime.xcframework/ios-arm64-simulator/libzx_document_ffi.a and b/bindings/ios/ZxDocumentRuntime.xcframework/ios-arm64-simulator/libzx_document_ffi.a differ diff --git a/bindings/ios/ZxDocumentRuntime.xcframework/ios-arm64/libzx_document_ffi.a b/bindings/ios/ZxDocumentRuntime.xcframework/ios-arm64/libzx_document_ffi.a index 4170bf7..08d635c 100644 Binary files a/bindings/ios/ZxDocumentRuntime.xcframework/ios-arm64/libzx_document_ffi.a and b/bindings/ios/ZxDocumentRuntime.xcframework/ios-arm64/libzx_document_ffi.a differ diff --git a/bindings/ios/device/libzx_document_ffi.a b/bindings/ios/device/libzx_document_ffi.a index 4170bf7..08d635c 100644 Binary files a/bindings/ios/device/libzx_document_ffi.a and b/bindings/ios/device/libzx_document_ffi.a differ diff --git a/bindings/ios/generated/zx_document.swift b/bindings/ios/generated/zx_document.swift index 0c01afd..126d0aa 100644 --- a/bindings/ios/generated/zx_document.swift +++ b/bindings/ios/generated/zx_document.swift @@ -416,6 +416,22 @@ fileprivate final class UniffiHandleMap: @unchecked Sendable { // Public interface members begin here. +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt8: FfiConverterPrimitive { + typealias FfiType = UInt8 + typealias SwiftType = UInt8 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt8 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: UInt8, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -480,6 +496,30 @@ fileprivate struct FfiConverterFloat: FfiConverterPrimitive { } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterBool : FfiConverter { + typealias FfiType = Int8 + typealias SwiftType = Bool + + public static func lift(_ value: Int8) throws -> Bool { + return value != 0 + } + + public static func lower(_ value: Bool) -> Int8 { + return value ? 1 : 0 + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool { + return try lift(readInt(&buf)) + } + + public static func write(_ value: Bool, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -781,6 +821,151 @@ public func FfiConverterTypeTextStats_lower(_ value: TextStats) -> RustBuffer { return FfiConverterTypeTextStats.lower(value) } +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum DocumentBlock: Equatable, Hashable { + + case heading(id: String, level: UInt8, text: String + ) + case paragraph(id: String, text: String + ) + case list(id: String, ordered: Bool, items: [String] + ) + case codeBlock(id: String, language: String?, code: String + ) + case quote(id: String, text: String + ) + case table(id: String, headers: [String], rows: [[String]] + ) + case imageBlock(id: String, src: String, alt: String? + ) + case horizontalRule(id: String + ) + + + + + +} + +#if compiler(>=6) +extension DocumentBlock: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeDocumentBlock: FfiConverterRustBuffer { + typealias SwiftType = DocumentBlock + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DocumentBlock { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .heading(id: try FfiConverterString.read(from: &buf), level: try FfiConverterUInt8.read(from: &buf), text: try FfiConverterString.read(from: &buf) + ) + + case 2: return .paragraph(id: try FfiConverterString.read(from: &buf), text: try FfiConverterString.read(from: &buf) + ) + + case 3: return .list(id: try FfiConverterString.read(from: &buf), ordered: try FfiConverterBool.read(from: &buf), items: try FfiConverterSequenceString.read(from: &buf) + ) + + case 4: return .codeBlock(id: try FfiConverterString.read(from: &buf), language: try FfiConverterOptionString.read(from: &buf), code: try FfiConverterString.read(from: &buf) + ) + + case 5: return .quote(id: try FfiConverterString.read(from: &buf), text: try FfiConverterString.read(from: &buf) + ) + + case 6: return .table(id: try FfiConverterString.read(from: &buf), headers: try FfiConverterSequenceString.read(from: &buf), rows: try FfiConverterSequenceSequenceString.read(from: &buf) + ) + + case 7: return .imageBlock(id: try FfiConverterString.read(from: &buf), src: try FfiConverterString.read(from: &buf), alt: try FfiConverterOptionString.read(from: &buf) + ) + + case 8: return .horizontalRule(id: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: DocumentBlock, into buf: inout [UInt8]) { + switch value { + + + case let .heading(id,level,text): + writeInt(&buf, Int32(1)) + FfiConverterString.write(id, into: &buf) + FfiConverterUInt8.write(level, into: &buf) + FfiConverterString.write(text, into: &buf) + + + case let .paragraph(id,text): + writeInt(&buf, Int32(2)) + FfiConverterString.write(id, into: &buf) + FfiConverterString.write(text, into: &buf) + + + case let .list(id,ordered,items): + writeInt(&buf, Int32(3)) + FfiConverterString.write(id, into: &buf) + FfiConverterBool.write(ordered, into: &buf) + FfiConverterSequenceString.write(items, into: &buf) + + + case let .codeBlock(id,language,code): + writeInt(&buf, Int32(4)) + FfiConverterString.write(id, into: &buf) + FfiConverterOptionString.write(language, into: &buf) + FfiConverterString.write(code, into: &buf) + + + case let .quote(id,text): + writeInt(&buf, Int32(5)) + FfiConverterString.write(id, into: &buf) + FfiConverterString.write(text, into: &buf) + + + case let .table(id,headers,rows): + writeInt(&buf, Int32(6)) + FfiConverterString.write(id, into: &buf) + FfiConverterSequenceString.write(headers, into: &buf) + FfiConverterSequenceSequenceString.write(rows, into: &buf) + + + case let .imageBlock(id,src,alt): + writeInt(&buf, Int32(7)) + FfiConverterString.write(id, into: &buf) + FfiConverterString.write(src, into: &buf) + FfiConverterOptionString.write(alt, into: &buf) + + + case let .horizontalRule(id): + writeInt(&buf, Int32(8)) + FfiConverterString.write(id, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeDocumentBlock_lift(_ buf: RustBuffer) throws -> DocumentBlock { + return try FfiConverterTypeDocumentBlock.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeDocumentBlock_lower(_ value: DocumentBlock) -> RustBuffer { + return FfiConverterTypeDocumentBlock.lower(value) +} + + public enum DocumentError: Swift.Error, Equatable, Hashable, Foundation.LocalizedError { @@ -1512,6 +1697,81 @@ fileprivate struct FfiConverterOptionTypeReadingPosition: FfiConverterRustBuffer } } } + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer { + typealias SwiftType = [String] + + public static func write(_ value: [String], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterString.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String] { + let len: Int32 = try readInt(&buf) + var seq = [String]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterString.read(from: &buf)) + } + return seq + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeDocumentBlock: FfiConverterRustBuffer { + typealias SwiftType = [DocumentBlock] + + public static func write(_ value: [DocumentBlock], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeDocumentBlock.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [DocumentBlock] { + let len: Int32 = try readInt(&buf) + var seq = [DocumentBlock]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeDocumentBlock.read(from: &buf)) + } + return seq + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceSequenceString: FfiConverterRustBuffer { + typealias SwiftType = [[String]] + + public static func write(_ value: [[String]], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterSequenceString.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [[String]] { + let len: Int32 = try readInt(&buf) + var seq = [[String]]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterSequenceString.read(from: &buf)) + } + return seq + } +} public func detectMaterialType(filePath: String)throws -> MaterialType { return try FfiConverterTypeMaterialType_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { uniffi_zx_document_ffi_fn_func_detect_material_type( @@ -1519,6 +1779,13 @@ public func detectMaterialType(filePath: String)throws -> MaterialType { ) }) } +public func parseMarkdown(content: String)throws -> [DocumentBlock] { + return try FfiConverterSequenceTypeDocumentBlock.lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { + uniffi_zx_document_ffi_fn_func_parse_markdown( + FfiConverterString.lower(content),$0 + ) +}) +} public func readImageMeta(filePath: String)throws -> ImageMeta { return try FfiConverterTypeImageMeta_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) { uniffi_zx_document_ffi_fn_func_read_image_meta( @@ -1552,6 +1819,9 @@ private let initializationResult: InitializationResult = { if (uniffi_zx_document_ffi_checksum_func_detect_material_type() != 55020) { return InitializationResult.apiChecksumMismatch } + if (uniffi_zx_document_ffi_checksum_func_parse_markdown() != 11780) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_zx_document_ffi_checksum_func_read_image_meta() != 62824) { return InitializationResult.apiChecksumMismatch } diff --git a/bindings/ios/simulator/libzx_document_ffi.a b/bindings/ios/simulator/libzx_document_ffi.a index f9e0d69..1cae318 100644 Binary files a/bindings/ios/simulator/libzx_document_ffi.a and b/bindings/ios/simulator/libzx_document_ffi.a differ diff --git a/crates/zx_document_ffi/src/lib.rs b/crates/zx_document_ffi/src/lib.rs index 41ec58e..ef84971 100644 --- a/crates/zx_document_ffi/src/lib.rs +++ b/crates/zx_document_ffi/src/lib.rs @@ -2,12 +2,56 @@ uniffi::setup_scaffolding!(); use std::sync::Arc; -// Re-export types so the UDL scaffolding can find them pub use zx_document_core::material_type::{MaterialType, PreviewMode}; pub use zx_document_core::image_meta::ImageMeta; pub use zx_document_core::text::TextStats; -// Flat DocumentError for UDL [Error] +use zx_document_core::blocks as core_blocks; + +// FFI-compatible DocumentBlock (tuple variants for UDL) +#[derive(Debug)] +pub enum DocumentBlock { + Heading(String, u8, String), + Paragraph(String, String), + List(String, bool, Vec), + CodeBlock(String, Option, String), + Quote(String, String), + Table(String, Vec, Vec>), + ImageBlock(String, String, Option), + HorizontalRule(String), +} + +impl From for DocumentBlock { + fn from(b: core_blocks::DocumentBlock) -> Self { + match b { + core_blocks::DocumentBlock::Heading { id, level, text } => { + DocumentBlock::Heading(id, level, text) + } + core_blocks::DocumentBlock::Paragraph { id, text } => { + DocumentBlock::Paragraph(id, text) + } + core_blocks::DocumentBlock::List { id, ordered, items } => { + DocumentBlock::List(id, ordered, items) + } + core_blocks::DocumentBlock::CodeBlock { id, language, code } => { + DocumentBlock::CodeBlock(id, language, code) + } + core_blocks::DocumentBlock::Quote { id, text } => { + DocumentBlock::Quote(id, text) + } + core_blocks::DocumentBlock::Table { id, headers, rows } => { + DocumentBlock::Table(id, headers, rows) + } + core_blocks::DocumentBlock::Image { id, src, alt } => { + DocumentBlock::ImageBlock(id, src, alt) + } + core_blocks::DocumentBlock::HorizontalRule { id } => { + DocumentBlock::HorizontalRule(id) + } + } + } +} + #[derive(Debug)] pub enum DocumentError { FileNotFound, @@ -43,7 +87,6 @@ impl From for DocumentError { } } -// FFI functions matching UDL namespace declarations fn detect_material_type(file_path: String) -> Result { zx_document_core::material_type::detect_material_type(&file_path).map_err(Into::into) } @@ -57,3 +100,11 @@ fn read_text_stats(file_path: String) -> Result, DocumentError> { let content = std::fs::read_to_string(&file_path).map_err(|_| DocumentError::FileNotFound)?; Ok(Arc::new(zx_document_core::text::text_stats(&content))) } + +fn parse_markdown(content: String) -> Result, DocumentError> { + let blocks = zx_document_core::markdown::parse_markdown(&content).map_err(|e| match e { + zx_document_core::error::DocumentError::ParseError(_) => DocumentError::ParseError, + _ => DocumentError::ParseError, + })?; + Ok(blocks.into_iter().map(Into::into).collect()) +} diff --git a/crates/zx_document_ffi/src/zx_document.udl b/crates/zx_document_ffi/src/zx_document.udl index 0b89c9c..e8bef0d 100644 --- a/crates/zx_document_ffi/src/zx_document.udl +++ b/crates/zx_document_ffi/src/zx_document.udl @@ -7,6 +7,9 @@ namespace zx_document { [Throws=DocumentError] TextStats read_text_stats([ByRef] string file_path); + + [Throws=DocumentError] + sequence parse_markdown([ByRef] string content); }; [Error] @@ -100,3 +103,15 @@ dictionary TextStats { u32 word_count; }; +[Enum] +interface DocumentBlock { + Heading(string id, u8 level, string text); + Paragraph(string id, string text); + List(string id, boolean ordered, sequence items); + CodeBlock(string id, string? language, string code); + Quote(string id, string text); + Table(string id, sequence headers, sequence> rows); + ImageBlock(string id, string src, string? alt); + HorizontalRule(string id); +}; +