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.
|
// 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)
|
#if swift(>=5.8)
|
||||||
@_documentation(visibility: private)
|
@_documentation(visibility: private)
|
||||||
#endif
|
#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)
|
#if swift(>=5.8)
|
||||||
@_documentation(visibility: private)
|
@_documentation(visibility: private)
|
||||||
#endif
|
#endif
|
||||||
@ -781,6 +821,151 @@ public func FfiConverterTypeTextStats_lower(_ value: TextStats) -> RustBuffer {
|
|||||||
return FfiConverterTypeTextStats.lower(value)
|
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 {
|
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 {
|
public func detectMaterialType(filePath: String)throws -> MaterialType {
|
||||||
return try FfiConverterTypeMaterialType_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) {
|
return try FfiConverterTypeMaterialType_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) {
|
||||||
uniffi_zx_document_ffi_fn_func_detect_material_type(
|
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 {
|
public func readImageMeta(filePath: String)throws -> ImageMeta {
|
||||||
return try FfiConverterTypeImageMeta_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) {
|
return try FfiConverterTypeImageMeta_lift(try rustCallWithError(FfiConverterTypeDocumentError_lift) {
|
||||||
uniffi_zx_document_ffi_fn_func_read_image_meta(
|
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) {
|
if (uniffi_zx_document_ffi_checksum_func_detect_material_type() != 55020) {
|
||||||
return InitializationResult.apiChecksumMismatch
|
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) {
|
if (uniffi_zx_document_ffi_checksum_func_read_image_meta() != 62824) {
|
||||||
return InitializationResult.apiChecksumMismatch
|
return InitializationResult.apiChecksumMismatch
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@ -2,12 +2,56 @@ uniffi::setup_scaffolding!();
|
|||||||
|
|
||||||
use std::sync::Arc;
|
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::material_type::{MaterialType, PreviewMode};
|
||||||
pub use zx_document_core::image_meta::ImageMeta;
|
pub use zx_document_core::image_meta::ImageMeta;
|
||||||
pub use zx_document_core::text::TextStats;
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum DocumentError {
|
pub enum DocumentError {
|
||||||
FileNotFound,
|
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> {
|
fn detect_material_type(file_path: String) -> Result<MaterialType, DocumentError> {
|
||||||
zx_document_core::material_type::detect_material_type(&file_path).map_err(Into::into)
|
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)?;
|
let content = std::fs::read_to_string(&file_path).map_err(|_| DocumentError::FileNotFound)?;
|
||||||
Ok(Arc::new(zx_document_core::text::text_stats(&content)))
|
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]
|
[Throws=DocumentError]
|
||||||
TextStats read_text_stats([ByRef] string file_path);
|
TextStats read_text_stats([ByRef] string file_path);
|
||||||
|
|
||||||
|
[Throws=DocumentError]
|
||||||
|
sequence<DocumentBlock> parse_markdown([ByRef] string content);
|
||||||
};
|
};
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
@ -100,3 +103,15 @@ dictionary TextStats {
|
|||||||
u32 word_count;
|
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