How enable_server and disable_server Commands Manage Server Connectivity in the Coco App
The enable_server and disable_server commands in the Coco app orchestrate a five-layer architecture that updates the UI state, persists configuration changes, and synchronizes the search source registry by routing calls from React hooks through TypeScript adapters to Rust backend commands.
The Coco app from infinilabs/coco-app manages server connectivity through a coordinated stack spanning the frontend UI, platform abstraction layer, and Rust backend. These enable_server and disable_server commands ensure that enabling or disabling a server updates the local store, persists the configuration to disk, and registers or deregisters the server with the search subsystem. The implementation guarantees consistency across the React state, Tauri runtime, and persistent storage.
Command Flow Architecture
The implementation spans five distinct layers, each responsible for a specific aspect of the command execution pipeline.
UI Layer: The useServers Hook
The entry point for toggling server connectivity resides in src/hooks/useServers.ts. This React hook provides the enableServer and disableServer callbacks that components use to initiate state changes.
The hook retrieves the current window's service, validates its ID, and routes the request to the platform adapter:
const enableServer = useCallback(async (enabled: boolean) => {
const service = await getCurrentWindowService();
if (!service?.id) throw new Error("No current service selected");
if (enabled) {
await platformAdapter.commands("enable_server", service.id);
} else {
await platformAdapter.commands("disable_server", service.id);
}
await setCurrentWindowService({ ...service, enabled });
await getAllServerList();
}, []);
After invoking the platform command, the hook updates the local store via setCurrentWindowService and refreshes the full server list to synchronize the UI.
Platform Abstraction: platformAdapter
The src/utils/platformAdapter.ts file exposes a unified API that works across different runtime environments. Currently, the app uses the Tauri adapter, which forwards all commands to the native backend.
import { createTauriAdapter } from "./tauriAdapter";
let platformAdapter = createTauriAdapter();
export default platformAdapter;
This abstraction allows the UI to remain agnostic about whether it is running inside the Tauri shell or a web environment.
Command Resolution: tauriWrappers
The src/utils/wrappers/tauriWrappers.ts file contains the commandWrapper object that maps string command names to their concrete TypeScript implementations. This resolution layer bridges generic command invocations to specific functions.
export const commandWrapper = {
async commands<T>(commandName: string, ...args: any[]): Promise<T> {
if (commandName in commands) {
return (commands as any)[commandName](...args);
}
throw new Error(`Command ${commandName} not found`);
},
};
When the UI calls "enable_server", this wrapper resolves the string to the actual enable_server function exported from src/commands/servers.ts.
TypeScript Bridge: servers.ts
Located at src/commands/servers.ts, this layer provides thin wrappers that forward requests to the Tauri runtime via invokeWithErrorHandler. This utility handles authentication checks, error logging, and communication with the @tauri-apps/api/core module.
export function enable_server(id: string): Promise<void> {
return invokeWithErrorHandler(`enable_server`, { id });
}
export function disable_server(id: string): Promise<void> {
return invokeWithErrorHandler(`disable_server`, { id });
}
The invokeWithErrorHandler function (lines 37-84 in the same file) builds the final bridge to the Rust backend, ensuring consistent error handling across all server commands.
Rust Implementation: servers.rs
The actual state mutation occurs in src-tauri/src/server/servers.rs. Both commands are defined as async Tauri command handlers that update the server's enabled flag, persist the change, and manage the search source registry.
Enable Server Implementation:
#[tauri::command]
pub async fn enable_server(app_handle: AppHandle, id: String) -> Result<(), ()> {
let Some(mut server) = get_server_by_id(id.as_str()).await else {
panic!("Server {} does not exist!", id);
};
server.enabled = true;
save_server(&server).await;
try_register_server_to_search_source(app_handle.clone(), &server).await;
persist_servers(&app_handle).await.expect("failed to save servers");
Ok(())
}
Disable Server Implementation:
#[tauri::command]
pub async fn disable_server(app_handle: AppHandle, id: String) -> Result<(), ()> {
let Some(mut server) = get_server_by_id(id.as_str()).await else {
panic!("Server {} does not exist!", id);
};
server.enabled = false;
let registry = app_handle.state::<SearchSourceRegistry>();
registry.remove_source(id.as_str()).await;
save_server(&server).await;
persist_servers(&app_handle).await.expect("failed to save servers");
Ok(())
}
The enable_server command sets server.enabled = true, persists the server via save_server, registers it with the search subsystem via try_register_server_to_search_source, and then calls persist_servers to write the full server list to disk. Conversely, disable_server sets the flag to false, removes the source from the SearchSourceRegistry, and persists the updated configuration.
Practical Implementation Examples
Using the React Hook in a Component
Components interact with the command system through the useServers hook rather than calling commands directly:
import { useServers } from "@/hooks/useServers";
function ServerToggle({ serverId }: { serverId: string }) {
const { enableServer } = useServers();
const toggle = async (e: React.ChangeEvent<HTMLInputElement>) => {
await enableServer(e.target.checked);
};
return <input type="checkbox" checked={server.enabled} onChange={toggle} />;
}
Direct Platform Adapter Invocation
For utilities or non-React contexts, you can invoke commands directly through the platform adapter:
import platformAdapter from "@/utils/platformAdapter";
// Enable server with id "abc123"
await platformAdapter.commands("enable_server", "abc123");
// Disable the same server
await platformAdapter.commands("disable_server", "abc123");
Accessing the Command Wrapper Directly
When you need to bypass the platform abstraction for testing or specialized use cases:
import { commandWrapper } from "@/utils/wrappers/tauriWrappers";
await commandWrapper.commands<void>("enable_server", "abc123");
Summary
- The enable_server and disable_server commands traverse five architectural layers: React hooks (
useServers.ts), platform adapter (platformAdapter.ts), command wrapper (tauriWrappers.ts), TypeScript bridge (servers.ts), and Rust backend (servers.rs). - The Rust implementation in
src-tauri/src/server/servers.rsperforms the actual state mutation by setting theenabledboolean, persisting viasave_serverandpersist_servers, and managing theSearchSourceRegistry. - Enable operations register the server with the search subsystem via
try_register_server_to_search_source, while disable operations remove it viaregistry.remove_source(). - All changes propagate back to the UI through the hook's subsequent calls to
setCurrentWindowServiceandgetAllServerList, ensuring state consistency across the application.
Frequently Asked Questions
What happens when I call enable_server in the Coco app?
Calling enable_server triggers a chain that begins in src/hooks/useServers.ts and ends in the Rust backend. The command sets the server's enabled flag to true, persists the configuration to storage via persist_servers, and registers the server with the search source registry so it becomes available for search operations.
How does disable_server differ from enable_server in the backend?
While enable_server calls try_register_server_to_search_source to add the server to the SearchSourceRegistry, disable_server retrieves the registry from the app state and calls registry.remove_source(id.as_str()).await to immediately disconnect the server from the search subsystem before persisting the disabled state.
Where is the server connectivity state persisted?
The state is persisted in two phases: first, save_server(&server).await writes the individual server record, then persist_servers(&app_handle).await writes the complete server list to storage. Both operations occur in src-tauri/src/server/servers.rs after updating the enabled boolean.
Can I use these commands outside of the React UI?
Yes. While the useServers hook provides the standard interface, you can import platformAdapter from src/utils/platformAdapter.ts or commandWrapper from src/utils/wrappers/tauriWrappers.ts to invoke enable_server and disable_server directly from TypeScript utilities or other non-React contexts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →