Skip to content

Commit eee1053

Browse files
committed
feat(sdk): Expose create_instance2 function with more potions
This includes log path and content cache budget
1 parent 1b79eec commit eee1053

9 files changed

Lines changed: 642 additions & 30 deletions

File tree

crates/fff-c/include/fff.h

Lines changed: 454 additions & 15 deletions
Large diffs are not rendered by default.

crates/fff-c/src/lib.rs

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,26 +104,76 @@ fn default_i32(val: i32, default: i32) -> i32 {
104104
if val == 0 { default } else { val }
105105
}
106106

107-
/// Create a new file finder instance.
107+
/// Create a new file finder instance (legacy signature).
108+
///
109+
/// @deprecated prefer `fff_create_instance2`, which also exposes log file and
110+
/// cache-budget configuration. This function delegates to `fff_create_instance2`
111+
/// with NULL log paths and auto cache budget, so behaviour is unchanged.
112+
///
113+
/// ## Safety
114+
/// See `fff_create_instance2`.
115+
#[unsafe(no_mangle)]
116+
pub unsafe extern "C" fn fff_create_instance(
117+
base_path: *const c_char,
118+
frecency_db_path: *const c_char,
119+
history_db_path: *const c_char,
120+
use_unsafe_no_lock: bool,
121+
enable_mmap_cache: bool,
122+
enable_content_indexing: bool,
123+
watch: bool,
124+
ai_mode: bool,
125+
) -> *mut FffResult {
126+
unsafe {
127+
fff_create_instance2(
128+
base_path,
129+
frecency_db_path,
130+
history_db_path,
131+
use_unsafe_no_lock,
132+
enable_mmap_cache,
133+
enable_content_indexing,
134+
watch,
135+
ai_mode,
136+
std::ptr::null(),
137+
std::ptr::null(),
138+
0,
139+
0,
140+
0,
141+
)
142+
}
143+
}
144+
145+
/// Create a new file finder instance (v2, with full options).
108146
///
109147
/// Returns an opaque pointer that must be passed to all other `fff_*` calls
110148
/// and eventually freed with `fff_destroy`.
111149
///
112150
/// # Parameters
113151
///
114-
/// * `base_path` – directory to index (required)
115-
/// * `frecency_db_path` – path to frecency LMDB database (NULL/empty to skip)
116-
/// * `history_db_path` – path to query history LMDB database (NULL/empty to skip)
117-
/// * `use_unsafe_no_lock` – use MDB_NOLOCK for LMDB (useful in single-process setups)
118-
/// * `enable_mmap_cache` – pre-populate mmap caches after the initial scan
119-
/// * `enable_content_indexing` – build content index after the initial scan
120-
/// * `watch` – start a background file-system watcher for live updates
121-
/// * `ai_mode` – enable AI-agent optimizations (auto-track frecency on modifications)
152+
/// * `base_path` – directory to index (required)
153+
/// * `frecency_db_path` – frecency LMDB database path (NULL/empty to skip)
154+
/// * `history_db_path` – query history LMDB database path (NULL/empty to skip)
155+
/// * `use_unsafe_no_lock` – use MDB_NOLOCK for LMDB (useful in single-process setups)
156+
/// * `enable_mmap_cache` – pre-populate mmap caches after the initial scan
157+
/// * `enable_content_indexing` – build content index after the initial scan
158+
/// * `watch` – start a background file-system watcher for live updates
159+
/// * `ai_mode` – enable AI-agent optimizations
160+
/// * `log_file_path` – tracing log file path (NULL/empty to skip).
161+
/// Only the first successful call in a process installs the subscriber;
162+
/// subsequent calls are no-ops at the log layer.
163+
/// * `log_level` – `"trace"`, `"debug"`, `"info"`, `"warn"`, `"error"`
164+
/// (NULL/empty defaults to `"info"`). Ignored when `log_file_path` is not set.
165+
/// * `cache_budget_max_files` – content cache file-count cap (0 = auto)
166+
/// * `cache_budget_max_bytes` – content cache byte cap (0 = auto)
167+
/// * `cache_budget_max_file_size` – per-file byte cap (0 = auto)
168+
///
169+
/// When all three `cache_budget_*` values are 0 the budget is auto-computed
170+
/// from repo size after the initial scan. Otherwise an explicit budget is
171+
/// used: any field left at 0 falls back to its `unlimited()` default.
122172
///
123173
/// ## Safety
124174
/// String parameters must be valid null-terminated UTF-8 or NULL.
125175
#[unsafe(no_mangle)]
126-
pub unsafe extern "C" fn fff_create_instance(
176+
pub unsafe extern "C" fn fff_create_instance2(
127177
base_path: *const c_char,
128178
frecency_db_path: *const c_char,
129179
history_db_path: *const c_char,
@@ -132,12 +182,24 @@ pub unsafe extern "C" fn fff_create_instance(
132182
enable_content_indexing: bool,
133183
watch: bool,
134184
ai_mode: bool,
185+
log_file_path: *const c_char,
186+
log_level: *const c_char,
187+
cache_budget_max_files: u64,
188+
cache_budget_max_bytes: u64,
189+
cache_budget_max_file_size: u64,
135190
) -> *mut FffResult {
136191
let base_path_str = match unsafe { cstr_to_str(base_path) } {
137192
Some(s) if !s.is_empty() => s.to_string(),
138193
_ => return FffResult::err("base_path is null or empty"),
139194
};
140195

196+
if let Some(log_path) = unsafe { optional_cstr(log_file_path) } {
197+
let level = unsafe { optional_cstr(log_level) };
198+
if let Err(e) = fff::log::init_tracing(log_path, level) {
199+
return FffResult::err(&format!("Failed to init tracing: {}", e));
200+
}
201+
}
202+
141203
let frecency_path = unsafe { optional_cstr(frecency_db_path) }.map(|s| s.to_string());
142204
let history_path = unsafe { optional_cstr(history_db_path) }.map(|s| s.to_string());
143205

@@ -185,6 +247,12 @@ pub unsafe extern "C" fn fff_create_instance(
185247
FFFMode::Neovim
186248
};
187249

250+
let cache_budget = fff::ContentCacheBudget::from_overrides(
251+
cache_budget_max_files as usize,
252+
cache_budget_max_bytes,
253+
cache_budget_max_file_size,
254+
);
255+
188256
// Initialize file picker (writes directly into shared_picker)
189257
if let Err(e) = FilePicker::new_with_shared_state(
190258
shared_picker.clone(),
@@ -195,7 +263,7 @@ pub unsafe extern "C" fn fff_create_instance(
195263
enable_content_indexing,
196264
watch,
197265
mode,
198-
cache_budget: None,
266+
cache_budget,
199267
},
200268
) {
201269
return FffResult::err(&format!("Failed to init file picker: {}", e));

crates/fff-core/src/types.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,31 @@ impl ContentCacheBudget {
722722
}
723723
}
724724

725+
/// Build a budget from caller-supplied overrides.
726+
///
727+
/// Each argument is a cap; `0` means "use the library default for that
728+
/// cap" (inherits from [`Self::default`], which is `new_for_repo(30_000)`).
729+
/// Returns `None` when every cap is `0`, signalling to the picker that it
730+
/// should auto-size the budget from the final scanned file count rather
731+
/// than applying an explicit override.
732+
pub fn from_overrides(max_files: usize, max_bytes: u64, max_file_size: u64) -> Option<Self> {
733+
if max_files == 0 && max_bytes == 0 && max_file_size == 0 {
734+
return None;
735+
}
736+
737+
let mut budget = Self::default();
738+
if max_files > 0 {
739+
budget.max_files = max_files;
740+
}
741+
if max_bytes > 0 {
742+
budget.max_bytes = max_bytes;
743+
}
744+
if max_file_size > 0 {
745+
budget.max_file_size = max_file_size;
746+
}
747+
Some(budget)
748+
}
749+
725750
pub fn reset(&self) {
726751
self.cached_count.store(0, Ordering::Relaxed);
727752
self.cached_bytes.store(0, Ordering::Relaxed);

packages/fff-bun/src/ffi.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function grepModeToU8(mode?: string): number {
4444
}
4545

4646
const ffiDefinition = {
47-
fff_create_instance: {
47+
fff_create_instance2: {
4848
args: [
4949
FFIType.cstring, // base_path
5050
FFIType.cstring, // frecency_db_path
@@ -54,6 +54,11 @@ const ffiDefinition = {
5454
FFIType.bool, // enable_content_indexing
5555
FFIType.bool, // watch
5656
FFIType.bool, // ai_mode
57+
FFIType.cstring, // log_file_path
58+
FFIType.cstring, // log_level
59+
FFIType.u64, // cache_budget_max_files
60+
FFIType.u64, // cache_budget_max_bytes
61+
FFIType.u64, // cache_budget_max_file_size
5762
],
5863
returns: FFIType.ptr,
5964
},
@@ -425,9 +430,14 @@ export function ffiCreate(
425430
enableContentIndexing: boolean,
426431
watch: boolean,
427432
aiMode: boolean,
433+
logFilePath: string,
434+
logLevel: string,
435+
cacheBudgetMaxFiles: bigint,
436+
cacheBudgetMaxBytes: bigint,
437+
cacheBudgetMaxFileSize: bigint,
428438
): Result<NativeHandle> {
429439
const library = loadLibrary();
430-
const resultPtr = library.symbols.fff_create_instance(
440+
const resultPtr = library.symbols.fff_create_instance2(
431441
ptr(encodeString(basePath)),
432442
ptr(encodeString(frecencyDbPath)),
433443
ptr(encodeString(historyDbPath)),
@@ -436,6 +446,11 @@ export function ffiCreate(
436446
enableContentIndexing,
437447
watch,
438448
aiMode,
449+
ptr(encodeString(logFilePath)),
450+
ptr(encodeString(logLevel)),
451+
cacheBudgetMaxFiles,
452+
cacheBudgetMaxBytes,
453+
cacheBudgetMaxFileSize,
439454
);
440455

441456
if (resultPtr === null) {

packages/fff-bun/src/finder.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ export class FileFinder {
117117
!(options.disableContentIndexing ?? options.disableMmapCache ?? false),
118118
!(options.disableWatch ?? false),
119119
options.aiMode ?? false,
120+
options.logFilePath ?? "",
121+
options.logLevel ?? "",
122+
BigInt(options.cacheBudgetMaxFiles ?? 0),
123+
BigInt(options.cacheBudgetMaxBytes ?? 0),
124+
BigInt(options.cacheBudgetMaxFileSize ?? 0),
120125
);
121126

122127
if (!result.ok) {

packages/fff-bun/src/types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,26 @@ export interface InitOptions {
5050
disableWatch?: boolean;
5151
/** enables optimizations for AI agent assistants. Provide as true if running via mcp/agent */
5252
aiMode?: boolean;
53+
/**
54+
* Path to the tracing log file. When set, the shared FFF tracing subscriber
55+
* is installed on first init and file output is written here. Omit to leave
56+
* logging uninitialized.
57+
*/
58+
logFilePath?: string;
59+
/**
60+
* Log level for the tracing subscriber: "trace", "debug", "info", "warn",
61+
* or "error". Defaults to "info". Ignored when `logFilePath` is not set.
62+
*/
63+
logLevel?: "trace" | "debug" | "info" | "warn" | "error";
64+
/**
65+
* Override for the content cache file-count cap. When omitted, the picker
66+
* auto-sizes the budget from the final scanned file count.
67+
*/
68+
cacheBudgetMaxFiles?: number;
69+
/** Override for the content cache byte cap. See `cacheBudgetMaxFiles`. */
70+
cacheBudgetMaxBytes?: number;
71+
/** Override for the per-file byte cap in the content cache. */
72+
cacheBudgetMaxFileSize?: number;
5373
}
5474

5575
/**

packages/fff-node/src/ffi.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,16 @@ export function ffiCreate(
334334
enableContentIndexing: boolean,
335335
watch: boolean,
336336
aiMode: boolean,
337+
logFilePath: string,
338+
logLevel: string,
339+
cacheBudgetMaxFiles: number,
340+
cacheBudgetMaxBytes: number,
341+
cacheBudgetMaxFileSize: number,
337342
): Result<NativeHandle> {
338343
loadLibrary();
339344

340345
const { rawPtr, struct: structData } = callRaw(
341-
"fff_create_instance",
346+
"fff_create_instance2",
342347
[
343348
DataType.String, // base_path
344349
DataType.String, // frecency_db_path
@@ -348,6 +353,11 @@ export function ffiCreate(
348353
DataType.Boolean, // enable_content_indexing
349354
DataType.Boolean, // watch
350355
DataType.Boolean, // ai_mode
356+
DataType.String, // log_file_path
357+
DataType.String, // log_level
358+
DataType.U64, // cache_budget_max_files
359+
DataType.U64, // cache_budget_max_bytes
360+
DataType.U64, // cache_budget_max_file_size
351361
],
352362
[
353363
basePath,
@@ -358,6 +368,11 @@ export function ffiCreate(
358368
enableContentIndexing,
359369
watch,
360370
aiMode,
371+
logFilePath,
372+
logLevel,
373+
cacheBudgetMaxFiles,
374+
cacheBudgetMaxBytes,
375+
cacheBudgetMaxFileSize,
361376
],
362377
);
363378

@@ -367,7 +382,7 @@ export function ffiCreate(
367382
if (success) {
368383
const handle = structData.handle;
369384
if (isNullPointer(handle)) {
370-
return err("fff_create_instance returned null handle");
385+
return err("fff_create_instance2 returned null handle");
371386
}
372387
return { ok: true, value: handle };
373388
} else {

packages/fff-node/src/finder.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ export class FileFinder {
116116
!(options.disableContentIndexing ?? options.disableMmapCache ?? false),
117117
!(options.disableWatch ?? false),
118118
options.aiMode ?? false,
119+
options.logFilePath ?? "",
120+
options.logLevel ?? "",
121+
options.cacheBudgetMaxFiles ?? 0,
122+
options.cacheBudgetMaxBytes ?? 0,
123+
options.cacheBudgetMaxFileSize ?? 0,
119124
);
120125

121126
if (!result.ok) {

packages/fff-node/src/types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,26 @@ export interface InitOptions {
5151
disableWatch?: boolean;
5252
/** enables optimizations for AI agent assistants. Provide as true if running via mcp/agent */
5353
aiMode?: boolean;
54+
/**
55+
* Path to the tracing log file. When set, the shared FFF tracing subscriber
56+
* is installed on first init and file output is written here. Omit to leave
57+
* logging uninitialized.
58+
*/
59+
logFilePath?: string;
60+
/**
61+
* Log level for the tracing subscriber: "trace", "debug", "info", "warn",
62+
* or "error". Defaults to "info". Ignored when `logFilePath` is not set.
63+
*/
64+
logLevel?: "trace" | "debug" | "info" | "warn" | "error";
65+
/**
66+
* Override for the content cache file-count cap. When omitted, the picker
67+
* auto-sizes the budget from the final scanned file count.
68+
*/
69+
cacheBudgetMaxFiles?: number;
70+
/** Override for the content cache byte cap. See `cacheBudgetMaxFiles`. */
71+
cacheBudgetMaxBytes?: number;
72+
/** Override for the per-file byte cap in the content cache. */
73+
cacheBudgetMaxFileSize?: number;
5474
}
5575

5676
/**

0 commit comments

Comments
 (0)