Skip to content

BumpyClock/digests-core

Repository files navigation

ABOUTME: Workspace overview for digests-core shared parsing library. ABOUTME: Documents crates, FFI, and platform usage patterns.

digests-core

Rust workspace providing shared parsing primitives and a C ABI for multi-platform apps:

  • crates/feed: feed parsing (RSS/Atom/podcast) → DFeed ABI.
  • crates/hermes: ReaderView/article extraction and metadata (Hermes port) → DReaderView / DMetadata.
  • crates/ffi: C ABI surface over the parsers with arena-managed results.
  • crates/cli: developer CLI for feed parsing.

Building

cargo test -q    # run all tests
cargo build -p digests-ffi --release   # produce FFI library (libdigests_ffi.*)

## Documentation

Start with the documentation index and per-crate pages:

- `docs/index.md`
- `docs/overview.md`
- `docs/building.md`
- `docs/feed.md`
- `docs/hermes.md`
- `docs/ffi.md`
- `docs/cli.md`
- `docs/troubleshooting.md`

CLI usage (digests-cli)

Run the CLI from the workspace:

cargo run -p digests-cli -- https://example.com/feed.xml

Build and run the binary:

cargo build -p digests-cli --release
./target/release/digests-cli https://example.com/feed.xml

Common examples:

# Parse multiple feeds and emit an envelope
./target/release/digests-cli https://example.com/feed.xml https://example.com/podcast.xml

# Parse from stdin
cat feed.xml | ./target/release/digests-cli -

# Override the feed_url field when parsing a local file
./target/release/digests-cli --feed-url https://example.com/feed.xml ./local-copy.xml

Use --compact to emit compact JSON and --help for the full option list.

FFI Usage (C ABI)

Functions (blocking):

  • digests_extract_reader(url_ptr, url_len, html_ptr, html_len, out_err) -> DReaderArena*
  • digests_reader_result(arena) -> const DReaderView*
  • digests_free_reader(arena)
  • digests_extract_metadata(html_ptr, html_len, base_url_ptr, base_url_len, out_err) -> DMetaArena*
  • digests_metadata_result(arena) -> const DMetadata*
  • digests_free_metadata(arena)

All strings are UTF-8 slices (ptr+len, not null-terminated). Results live in an arena; free the arena when done. On success out_err->code == D_OK.

Platform Helpers (async wrappers)

FFI calls are synchronous; wrap them off the main thread:

Swift

// Suppose you expose C functions via module map.
func extractReaderAsync(url: String, html: Data, completion: @escaping (DReaderView?, DError) -> Void) {
    DispatchQueue.global(qos: .userInitiated).async {
        var err = DError(code: D_OK, message: DString(data: nil, len: 0))
        let arena = html.withUnsafeBytes { bytes in
            url.withCString { cUrl in
                digests_extract_reader(
                    UnsafeRawPointer(cUrl), url.utf8.count,
                    bytes.baseAddress, html.count,
                    &err
                )
            }
        }
        let view = arena.flatMap { digests_reader_result($0) }
        DispatchQueue.main.async {
            completion(view?.pointee, err)
            if let arena = arena { digests_free_reader(arena) }
        }
    }
}

Kotlin (JNI)

fun extractReaderAsync(url: String, html: ByteArray, callback: (ReaderView?, Int) -> Unit) {
    CoroutineScope(Dispatchers.IO).launch {
        val err = DError()
        val arena = digests_extract_reader(url, html, err)
        val view = arena?.let { digests_reader_result(it) }
        withContext(Dispatchers.Main) {
            callback(view, err.code)
            arena?.let { digests_free_reader(it) }
        }
    }
}

C# (P/Invoke)

public static Task<ReaderView?> ExtractReaderAsync(string url, byte[] html) =>
    Task.Run(() =>
    {
        var err = new DError();
        var arena = digests_extract_reader(url, html, ref err);
        var view = arena != IntPtr.Zero ? Marshal.PtrToStructure<ReaderView>(digests_reader_result(arena)) : (ReaderView?)null;
        if (arena != IntPtr.Zero) digests_free_reader(arena);
        return view;
    });

Adjust signatures to your actual FFI bindings; key point is to call the blocking C function on a background thread/queue/dispatcher, then marshal results back to the UI thread.

Metadata-only

digests_extract_metadata is inexpensive; still treat it as blocking and wrap similarly if calling from UI threads.

Notes

  • Strings are not null-terminated; always use len.
  • Arenas own all returned memory; do not free individual pointers.
  • Check DIGESTS_FFI_VERSION for ABI compatibility in your bindings.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages