Bridge layer exposing cc-core functionality as Tauri IPC commands.
The GraphState type wraps a Mutex<Option<CodeGraph>> and is registered as Tauri managed state. This eliminates the need to serialize the full graph over IPC for every command:
scan_repocreates the initial graph and stores it in stateparse_repotakes the graph from state, parses, stores it back, and returns itget_subgraphreads the graph from state without needing it passed over IPC
pub struct GraphState(pub Mutex<Option<CodeGraph>>);#[command]
pub async fn scan_repo(
path: String,
state: tauri::State<'_, GraphState>,
) -> Result<CodeGraph, String>Discovers directory structure and files in a repository. Stores the result in server-side state.
Parameters:
path- Absolute filesystem path to repository
Returns: CodeGraph with Directory and File nodes (no edges yet)
Errors:
- Path doesn't exist
- Path is not a directory
- Scanner errors
#[command]
pub async fn parse_repo(
path: String,
on_event: Channel<ParseEvent>,
state: tauri::State<'_, GraphState>,
) -> Result<CodeGraph, String>Parses source code, extracts code blocks, and resolves references into edges.
Reads the graph from server-side state (set by scan_repo), parses files in parallel
using rayon, and stores the updated graph back in state.
Parameters:
path- Repository root pathon_event- Streaming channel for progress events
Streaming Events:
enum ParseEvent {
FileStart { path: String },
FileDone { path: String, blocks: usize },
Error { path: String, message: String },
Complete { total_files: usize, total_blocks: usize },
}Returns: Enhanced CodeGraph with CodeBlock nodes and edges
#[command]
pub async fn get_subgraph(
visible_ids: Vec<String>,
edge_kinds: Vec<String>,
state: tauri::State<'_, GraphState>,
) -> Result<SubGraph, String>Filters graph to visible nodes and selected edge types. Reads the graph from server-side state.
Parameters:
visible_ids- Node IDs to includeedge_kinds- Edge types to include (e.g.,["Import", "FunctionCall"])
Returns: Filtered SubGraph for rendering
#[command]
pub async fn clone_github_repo(url: String) -> Result<String, String>Clones a GitHub/GitLab repository for analysis.
Parameters:
url- GitHub or GitLab HTTPS/SSH URL
Returns: Filesystem path to cloned repository
Frontend
│
├─ invoke("clone_github_repo", { url })
│ ↓
│ git clone --depth 1
│ ↓
│ Returns path
│
├─ invoke("scan_repo", { path })
│ ↓
│ RepoScanner::scan()
│ Store graph in GraphState
│ ↓
│ Returns CodeGraph (skeleton)
│
├─ invoke("parse_repo", { path, onEvent })
│ ↓
│ Take graph from GraphState
│ For each file (parallel via rayon):
│ ├─ Read source file
│ ├─ Extractor::extract_file()
│ Merge results sequentially:
│ ├─ Send FileStart
│ ├─ Add nodes to graph
│ ├─ Send FileDone
│ ↓
│ SymbolTable::build_from_graph()
│ SymbolTable::resolve_references()
│ Store graph in GraphState
│ ↓
│ Send Complete
│ Returns CodeGraph (full)
│
└─ invoke("get_subgraph", { visibleIds, edgeKinds })
↓
Read graph from GraphState
SubGraph::from_graph()
↓
Returns SubGraph
All data passes through JSON via serde:
- Frontend calls
invoke()with JSON arguments - Backend deserializes to Rust types
- Backend returns
Result<T, String> - Tauri serializes result to JSON
- Frontend receives as TypeScript object
Server-side state: The graph is kept in memory on the backend via GraphState.
Commands read/write it directly, avoiding repeated JSON serialization of the full graph.
Skipped fields (not serialized):
CodeGraph.forward_adjCodeGraph.reverse_adjCodeGraph.edge_dedup
These indexes are rebuilt after deserialization via rebuild_adjacency().
All commands return Result<T, String>:
- Success: serialized value
- Error: human-readable message
Per-file errors during parse_repo are streamed as ParseEvent::Error and processing continues.
// packages/app/src/api/commands.ts
import { invoke, Channel } from "@tauri-apps/api/core";
export async function scanRepo(path: string): Promise<CodeGraph> {
return invoke("scan_repo", { path });
}
export async function parseRepo(
path: string,
onEvent: (event: ParseEvent) => void
): Promise<CodeGraph> {
const channel = new Channel<ParseEvent>();
channel.onmessage = onEvent;
return invoke("parse_repo", {
path,
onEvent: channel,
});
}
export async function getSubgraph(
visibleIds: string[],
edgeKinds: string[]
): Promise<SubGraph> {
return invoke("get_subgraph", {
visibleIds,
edgeKinds,
});
}
export async function cloneGithubRepo(url: string): Promise<string> {
return invoke("clone_github_repo", { url });
}Note: The graph is no longer passed from the frontend to parseRepo or getSubgraph.
The backend maintains the graph in server-side state, eliminating the overhead of
serializing the full graph to JSON for each IPC call.
| Crate | Purpose |
|---|---|
| cc-core | Core parsing/graph logic |
| tauri | Tauri framework |
| serde/serde_json | Serialization |
| tracing | Logging |
| rayon | Parallel file parsing |