feat: expose parse_markdown via FFI - returns [DocumentBlock] with all 8 block types
This commit is contained in:
parent
cfbee9ea53
commit
f8c0864b61
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -416,6 +416,22 @@ fileprivate final class UniffiHandleMap<T>: @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
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -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<String>),
|
||||
CodeBlock(String, Option<String>, String),
|
||||
Quote(String, String),
|
||||
Table(String, Vec<String>, Vec<Vec<String>>),
|
||||
ImageBlock(String, String, Option<String>),
|
||||
HorizontalRule(String),
|
||||
}
|
||||
|
||||
impl From<core_blocks::DocumentBlock> 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<zx_document_core::error::DocumentError> for DocumentError {
|
||||
}
|
||||
}
|
||||
|
||||
// FFI functions matching UDL namespace declarations
|
||||
fn detect_material_type(file_path: String) -> Result<MaterialType, DocumentError> {
|
||||
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<Arc<TextStats>, 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<Vec<DocumentBlock>, 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())
|
||||
}
|
||||
|
||||
@ -7,6 +7,9 @@ namespace zx_document {
|
||||
|
||||
[Throws=DocumentError]
|
||||
TextStats read_text_stats([ByRef] string file_path);
|
||||
|
||||
[Throws=DocumentError]
|
||||
sequence<DocumentBlock> 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<string> items);
|
||||
CodeBlock(string id, string? language, string code);
|
||||
Quote(string id, string text);
|
||||
Table(string id, sequence<string> headers, sequence<sequence<string>> rows);
|
||||
ImageBlock(string id, string src, string? alt);
|
||||
HorizontalRule(string id);
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user