# kglite-c — C ABI for kglite

Stable `extern "C"` surface over the [kglite](https://crates.io/crates/kglite)
knowledge graph engine. Non-Rust bindings (Go via cgo, JavaScript via
napi, JVM via JNI, .NET via P/Invoke) consume a single C header
(`include/kglite.h`) rather than re-implementing wrappers in their
host language.

This crate is glue. The engine itself (Cypher pipeline, transaction
model, storage backends, dataset loaders) lives in the sibling
`kglite` crate. `kglite-c` exposes a curated subset of
`kglite::api::*` via `#[no_mangle] extern "C"` functions plus
cbindgen-generated header.

## Status: Phase H.2 (skeleton)

This is the initial skeleton — top-12 entry points (lifecycle /
session / Cypher / result accessors / error introspection / ABI
version). See `docs/rust/c-abi.md` for the design conventions and
the full Phase H roadmap (H.3 datasets + embedder, H.4 Go PoC
consumer, H.5 release coordination).

## Use from C

```c
#include <stdio.h>
#include "kglite.h"

int main(void) {
    KgliteGraph* graph = NULL;
    const char* err = NULL;
    KgliteStatusCode rc = kglite_load_file("graph.kgl", &graph, &err);
    if (rc != KGLITE_OK) {
        fprintf(stderr, "load failed: %s\n", err);
        kglite_free_string(err);
        return 1;
    }

    KgliteSession* session = NULL;
    kglite_session_new(graph, &session);
    // session takes ownership of graph; do NOT call kglite_graph_free

    KgliteCypherResult* result = NULL;
    rc = kglite_session_execute_read(
        session,
        "MATCH (n) RETURN count(n)",
        NULL,                       // no params
        &result,
        &err
    );
    if (rc != KGLITE_OK) {
        fprintf(stderr, "query failed: %s\n", err);
        kglite_free_string(err);
        kglite_session_free(session);
        return rc;
    }

    const char* cols = kglite_cypher_result_columns_json(result);
    const char* rows = kglite_cypher_result_rows_json(result);
    printf("columns: %s\n", cols);
    printf("rows: %s\n", rows);
    kglite_free_string(cols);
    kglite_free_string(rows);

    kglite_cypher_result_free(result);
    kglite_session_free(session);
    return 0;
}
```

## Use from Go (sketch — full Phase H.4 PoC pending)

```go
// #cgo LDFLAGS: -lkglite_c
// #include <kglite.h>
import "C"

func main() {
    var graph *C.KgliteGraph
    var errMsg *C.char
    rc := C.kglite_load_file(C.CString("graph.kgl"), &graph, &errMsg)
    // ...
}
```

## Memory ownership

Every function documents who owns what. The rules:

- Arguments by `*const c_char` / `*const T` — borrowed for the call.
- Arguments by `*mut T` (opaque handle) — borrowed for the call,
  caller still owns.
- Return values by `*mut T` — caller OWNS, must free via
  `kglite_<type>_free`.
- Return values by `*const c_char` — caller OWNS, must free via
  `kglite_free_string`.
- Return values by-value primitives — no ownership concern.

## Error handling

errno-style: every fallible function returns `KgliteStatusCode`
(`KGLITE_OK == 0` on success), with out-params for both the result
handle and an optional error message string. The error message,
when present, is owned and must be freed via `kglite_free_string`.

`KgliteStatusCode` variants 1-16 map 1:1 to `kglite::api::KgErrorCode`
variants. Bindings can pull the canonical human-readable name,
the Neo4j `Neo.ClientError.*` status code, or the HTTP status code
via:

```c
const char* kglite_status_code_name(KgliteStatusCode);
const char* kglite_status_code_neo4j_status(KgliteStatusCode);
uint16_t    kglite_status_code_http_status(KgliteStatusCode);
```

## Sync only

The C ABI is fully synchronous. Bindings own their own async/threading
model — Go uses goroutines, JS uses worker threads, JVM uses thread
pools, each wrapping the sync C calls. Async dataset fetchers
(`kglite::api::datasets::*`) are exposed via their `*_blocking`
companions in Phase H.3.

## Versioning

`kglite-c` versions track `kglite`'s minor version. The
`kglite_abi_version()` function returns the runtime ABI version
for binding-author sanity checks.

## License

MIT (matches `kglite`).
