feat(fff-c): add stable C accessor functions for external FFI consumers#395
Merged
dmtrKovalenko merged 3 commits intodmtrKovalenko:mainfrom Apr 20, 2026
Merged
Conversation
Tests cover: - Null-pointer guards: every getter returns its zero-value (0 / false / null) when passed a null pointer — no UB, no crash - Data correctness: FffFileItem, FffGrepMatch, FffSearchResult and FffGrepResult getters return exactly the values stored in the struct All 8 tests pass clean under Rust 2024 edition (no unsafe-op warnings). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dmtrKovalenko
approved these changes
Apr 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
fff-cis already an editor-agnostic C library — the README mentions it can be used from any language. However, the only way external consumers (Emacs Lisp, Pythonctypes, scripts) can currently access struct fields is by computing byte offsets manually. This is silently fragile: any new field shifts every subsequent offset with zero compile-time signal.This is not theoretical. JonasThowsen/fff.el is an existing Emacs integration that uses
libfff_cdirectly via FFI, and its hardcoded offsets are already wrong against currentmain:fff.elreadsline_contentFffMatchRange*(pointer to struct!)line_numberbyte_offsetcolcontext_before_countThe struct grew (added
file_name,git_status,match_ranges, context arrays) between whenfff.elwas written and today, pushing all subsequent field offsets with no signal to the caller.Change
Adds
crates/fff-c/src/accessors.rswith C-exported getter functions for every field on:FffFileItem—relative_path,file_name,git_status,size,modified, frecency scores,is_binaryFffGrepMatch— all of the above plusline_content,line_number,col,byte_offset, match ranges, context arrays,fuzzy_score,is_definitionFffSearchResult—count,total_matched,total_filesFffGrepResult—count,total_matched,total_files_searched,total_files,filtered_file_count,next_file_offset,regex_fallback_errorcbindgenpicks these up automatically — no changes tocbindgen.tomlneeded.Zero impact on the Neovim integration — Lua uses
ffi.cdefwhich parses the full header and is unaffected by new functions.Why accessor functions instead of just
repr(C)guaranteesrepr(C)prevents the Rust compiler from reordering fields, but does not prevent adding new fields between existing ones. Accessor functions make the struct layout a true implementation detail — callers bind to a symbol name once and are immune to layout changes forever.Safety
Every function has an explicit null-pointer guard (returns
0/false/null) — no UB on null input. All functions are covered by unit tests:Usage example (Emacs Lisp)
A companion PR will follow at JonasThowsen/fff.el migrating from hardcoded offsets to these accessor functions.